v3-4-test: Protect against invalid winbindd_cache entries
[obnox/samba-ctdb.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
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
11
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.
16    
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.
21    
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/>.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "tdb_validate.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 #define WINBINDD_CACHE_VERSION 1
34 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
35
36 extern struct winbindd_methods reconnect_methods;
37 #ifdef HAVE_ADS
38 extern struct winbindd_methods ads_methods;
39 #endif
40 extern struct winbindd_methods builtin_passdb_methods;
41
42 /*
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.
46  */
47
48 static const char *non_centry_keys[] = {
49         "SEQNUM/",
50         "DR/",
51         "DE/",
52         "WINBINDD_OFFLINE",
53         WINBINDD_CACHE_VERSION_KEYSTR,
54         NULL
55 };
56
57 /************************************************************************
58  Is this key a non-centry type ?
59 ************************************************************************/
60
61 static bool is_non_centry_key(TDB_DATA kbuf)
62 {
63         int i;
64
65         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
66                 return false;
67         }
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;
72                 }
73                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
74                         return true;
75                 }
76         }
77         return false;
78 }
79
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. */
83
84 static bool global_winbindd_offline_state;
85
86 struct winbind_cache {
87         TDB_CONTEXT *tdb;
88 };
89
90 struct cache_entry {
91         NTSTATUS status;
92         uint32 sequence_number;
93         uint8 *data;
94         uint32 len, ofs;
95 };
96
97 void (*smb_panic_fn)(const char *const why) = smb_panic;
98
99 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
100
101 static struct winbind_cache *wcache;
102
103 void winbindd_check_cache_size(time_t t)
104 {
105         static time_t last_check_time;
106         struct stat st;
107
108         if (last_check_time == (time_t)0)
109                 last_check_time = t;
110
111         if (t - last_check_time < 60 && t - last_check_time > 0)
112                 return;
113
114         if (wcache == NULL || wcache->tdb == NULL) {
115                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
116                 return;
117         }
118
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;
122         }
123
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();
129         }
130 }
131
132 /* get the winbind_cache structure */
133 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
134 {
135         struct winbind_cache *ret = wcache;
136
137         /* We have to know what type of domain we are dealing with first. */
138
139         if (domain->internal) {
140                 domain->backend = &builtin_passdb_methods;
141                 domain->initialized = True;
142         }
143         if ( !domain->initialized ) {
144                 init_dc_connection( domain );
145         }
146
147         /* 
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
153
154            For (a) we can always contact the trusted domain using krb5 
155            since we have the domain trust account password
156
157            For (b) we can only use RPC since we have no way of 
158            getting a krb5 ticket in our own domain
159
160            For (c) we can always use krb5 since we have a kerberos trust
161
162            --jerry
163          */
164
165         if (!domain->backend) {
166 #ifdef HAVE_ADS
167                 struct winbindd_domain *our_domain = domain;
168
169                 /* find our domain first so we can figure out if we 
170                    are joined to a kerberized domain */
171
172                 if ( !domain->primary )
173                         our_domain = find_our_domain();
174
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
185                 }
186 #endif  /* HAVE_ADS */
187         }
188
189         if (ret)
190                 return ret;
191         
192         ret = SMB_XMALLOC_P(struct winbind_cache);
193         ZERO_STRUCTP(ret);
194
195         wcache = ret;
196         wcache_flush_cache();
197
198         return ret;
199 }
200
201 /*
202   free a centry structure
203 */
204 static void centry_free(struct cache_entry *centry)
205 {
206         if (!centry)
207                 return;
208         SAFE_FREE(centry->data);
209         free(centry);
210 }
211
212 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
213 {
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;
219         }
220         return true;
221 }
222
223 /*
224   pull a uint32 from a cache entry 
225 */
226 static uint32 centry_uint32(struct cache_entry *centry)
227 {
228         uint32 ret;
229
230         if (!centry_check_bytes(centry, 4)) {
231                 smb_panic_fn("centry_uint32");
232         }
233         ret = IVAL(centry->data, centry->ofs);
234         centry->ofs += 4;
235         return ret;
236 }
237
238 /*
239   pull a uint16 from a cache entry 
240 */
241 static uint16 centry_uint16(struct cache_entry *centry)
242 {
243         uint16 ret;
244         if (!centry_check_bytes(centry, 2)) {
245                 smb_panic_fn("centry_uint16");
246         }
247         ret = CVAL(centry->data, centry->ofs);
248         centry->ofs += 2;
249         return ret;
250 }
251
252 /*
253   pull a uint8 from a cache entry 
254 */
255 static uint8 centry_uint8(struct cache_entry *centry)
256 {
257         uint8 ret;
258         if (!centry_check_bytes(centry, 1)) {
259                 smb_panic_fn("centry_uint8");
260         }
261         ret = CVAL(centry->data, centry->ofs);
262         centry->ofs += 1;
263         return ret;
264 }
265
266 /*
267   pull a NTTIME from a cache entry 
268 */
269 static NTTIME centry_nttime(struct cache_entry *centry)
270 {
271         NTTIME ret;
272         if (!centry_check_bytes(centry, 8)) {
273                 smb_panic_fn("centry_nttime");
274         }
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;
280 }
281
282 /*
283   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
284 */
285 static time_t centry_time(struct cache_entry *centry)
286 {
287         return (time_t)centry_nttime(centry);
288 }
289
290 /* pull a string from a cache entry, using the supplied
291    talloc context 
292 */
293 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
294 {
295         uint32 len;
296         char *ret;
297
298         len = centry_uint8(centry);
299
300         if (len == 0xFF) {
301                 /* a deliberate NULL string */
302                 return NULL;
303         }
304
305         if (!centry_check_bytes(centry, (size_t)len)) {
306                 smb_panic_fn("centry_string");
307         }
308
309         ret = TALLOC_ARRAY(mem_ctx, char, len+1);
310         if (!ret) {
311                 smb_panic_fn("centry_string out of memory\n");
312         }
313         memcpy(ret,centry->data + centry->ofs, len);
314         ret[len] = 0;
315         centry->ofs += len;
316         return ret;
317 }
318
319 /* pull a hash16 from a cache entry, using the supplied
320    talloc context 
321 */
322 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
323 {
324         uint32 len;
325         char *ret;
326
327         len = centry_uint8(centry);
328
329         if (len != 16) {
330                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
331                         len ));
332                 return NULL;
333         }
334
335         if (!centry_check_bytes(centry, 16)) {
336                 return NULL;
337         }
338
339         ret = TALLOC_ARRAY(mem_ctx, char, 16);
340         if (!ret) {
341                 smb_panic_fn("centry_hash out of memory\n");
342         }
343         memcpy(ret,centry->data + centry->ofs, 16);
344         centry->ofs += 16;
345         return ret;
346 }
347
348 /* pull a sid from a cache entry, using the supplied
349    talloc context 
350 */
351 static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
352 {
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;
357         }
358         return true;
359 }
360
361
362 /*
363   pull a NTSTATUS from a cache entry
364 */
365 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
366 {
367         NTSTATUS status;
368
369         status = NT_STATUS(centry_uint32(centry));
370         return status;
371 }
372
373
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)
376 {
377         bool ret;
378
379         if (!wcache->tdb)
380                 return false;
381
382         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
383
384         if (ret)
385                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
386                         domain->name ));
387         return ret;
388 }
389
390 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
391 {
392         TDB_DATA data;
393         fstring key;
394         uint32 time_diff;
395         
396         if (!wcache->tdb) {
397                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
398                 return NT_STATUS_UNSUCCESSFUL;
399         }
400                 
401         fstr_sprintf( key, "SEQNUM/%s", domain->name );
402         
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;
407         }
408         
409         domain->sequence_number = IVAL(data.dptr, 0);
410         domain->last_seq_check  = IVAL(data.dptr, 4);
411         
412         SAFE_FREE(data.dptr);
413
414         /* have we expired? */
415         
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;
422         }
423
424         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
425                 domain->name, domain->sequence_number, 
426                 (uint32)domain->last_seq_check));
427
428         return NT_STATUS_OK;
429 }
430
431 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
432 {
433         TDB_DATA data;
434         fstring key_str;
435         uint8 buf[8];
436         
437         if (!wcache->tdb) {
438                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
439                 return NT_STATUS_UNSUCCESSFUL;
440         }
441                 
442         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
443         
444         SIVAL(buf, 0, domain->sequence_number);
445         SIVAL(buf, 4, domain->last_seq_check);
446         data.dptr = buf;
447         data.dsize = 8;
448         
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;
452         }
453
454         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
455                 domain->name, domain->sequence_number, 
456                 (uint32)domain->last_seq_check));
457         
458         return NT_STATUS_OK;
459 }
460
461 /*
462   refresh the domain sequence number. If force is true
463   then always refresh it, no matter how recently we fetched it
464 */
465
466 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
467 {
468         NTSTATUS status;
469         unsigned time_diff;
470         time_t t = time(NULL);
471         unsigned cache_time = lp_winbind_cache_time();
472
473         if ( IS_DOMAIN_OFFLINE(domain) ) {
474                 return;
475         }
476         
477         get_cache( domain );
478
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;
483         }
484 #endif
485
486         time_diff = t - domain->last_seq_check;
487
488         /* see if we have to refetch the domain sequence number */
489         if (!force && (time_diff < cache_time) &&
490                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
491                         NT_STATUS_IS_OK(domain->last_status)) {
492                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
493                 goto done;
494         }
495         
496         /* try to get the sequence number from the tdb cache first */
497         /* this will update the timestamp as well */
498         
499         status = fetch_cache_seqnum( domain, t );
500         if (NT_STATUS_IS_OK(status) &&
501                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
502                         NT_STATUS_IS_OK(domain->last_status)) {
503                 goto done;
504         }
505
506         /* important! make sure that we know if this is a native 
507            mode domain or not.  And that we can contact it. */
508
509         if ( winbindd_can_contact_domain( domain ) ) {          
510                 status = domain->backend->sequence_number(domain, 
511                                                           &domain->sequence_number);
512         } else {
513                 /* just use the current time */
514                 status = NT_STATUS_OK;
515                 domain->sequence_number = time(NULL);
516         }
517
518
519         /* the above call could have set our domain->backend to NULL when
520          * coming from offline to online mode, make sure to reinitialize the
521          * backend - Guenther */
522         get_cache( domain );
523
524         if (!NT_STATUS_IS_OK(status)) {
525                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
526                 domain->sequence_number = DOM_SEQUENCE_NONE;
527         }
528         
529         domain->last_status = status;
530         domain->last_seq_check = time(NULL);
531         
532         /* save the new sequence number in the cache */
533         store_cache_seqnum( domain );
534
535 done:
536         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
537                    domain->name, domain->sequence_number));
538
539         return;
540 }
541
542 /*
543   decide if a cache entry has expired
544 */
545 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
546 {
547         /* If we've been told to be offline - stay in that state... */
548         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
549                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
550                         keystr, domain->name ));
551                 return false;
552         }
553
554         /* when the domain is offline return the cached entry.
555          * This deals with transient offline states... */
556
557         if (!domain->online) {
558                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
559                         keystr, domain->name ));
560                 return false;
561         }
562
563         /* if the server is OK and our cache entry came from when it was down then
564            the entry is invalid */
565         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
566             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
567                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
568                         keystr, domain->name ));
569                 return true;
570         }
571
572         /* if the server is down or the cache entry is not older than the
573            current sequence number then it is OK */
574         if (wcache_server_down(domain) || 
575             centry->sequence_number == domain->sequence_number) {
576                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
577                         keystr, domain->name ));
578                 return false;
579         }
580
581         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
582                 keystr, domain->name ));
583
584         /* it's expired */
585         return true;
586 }
587
588 static struct cache_entry *wcache_fetch_raw(char *kstr)
589 {
590         TDB_DATA data;
591         struct cache_entry *centry;
592         TDB_DATA key;
593
594         key = string_tdb_data(kstr);
595         data = tdb_fetch(wcache->tdb, key);
596         if (!data.dptr) {
597                 /* a cache miss */
598                 return NULL;
599         }
600
601         centry = SMB_XMALLOC_P(struct cache_entry);
602         centry->data = (unsigned char *)data.dptr;
603         centry->len = data.dsize;
604         centry->ofs = 0;
605
606         if (centry->len < 8) {
607                 /* huh? corrupt cache? */
608                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
609                 centry_free(centry);
610                 return NULL;
611         }
612         
613         centry->status = centry_ntstatus(centry);
614         centry->sequence_number = centry_uint32(centry);
615
616         return centry;
617 }
618
619 /*
620   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
621   number and return status
622 */
623 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
624                                         struct winbindd_domain *domain,
625                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
626 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
627                                         struct winbindd_domain *domain,
628                                         const char *format, ...)
629 {
630         va_list ap;
631         char *kstr;
632         struct cache_entry *centry;
633
634         if (!winbindd_use_cache()) {
635                 return NULL;
636         }
637
638         refresh_sequence_number(domain, false);
639
640         va_start(ap, format);
641         smb_xvasprintf(&kstr, format, ap);
642         va_end(ap);
643
644         centry = wcache_fetch_raw(kstr);
645         if (centry == NULL) {
646                 free(kstr);
647                 return NULL;
648         }
649
650         if (centry_expired(domain, kstr, centry)) {
651
652                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
653                          kstr, domain->name ));
654
655                 centry_free(centry);
656                 free(kstr);
657                 return NULL;
658         }
659
660         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
661                  kstr, domain->name ));
662
663         free(kstr);
664         return centry;
665 }
666
667 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
668 static void wcache_delete(const char *format, ...)
669 {
670         va_list ap;
671         char *kstr;
672         TDB_DATA key;
673
674         va_start(ap, format);
675         smb_xvasprintf(&kstr, format, ap);
676         va_end(ap);
677
678         key = string_tdb_data(kstr);
679
680         tdb_delete(wcache->tdb, key);
681         free(kstr);
682 }
683
684 /*
685   make sure we have at least len bytes available in a centry 
686 */
687 static void centry_expand(struct cache_entry *centry, uint32 len)
688 {
689         if (centry->len - centry->ofs >= len)
690                 return;
691         centry->len *= 2;
692         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
693                                          centry->len);
694         if (!centry->data) {
695                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
696                 smb_panic_fn("out of memory in centry_expand");
697         }
698 }
699
700 /*
701   push a uint32 into a centry 
702 */
703 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
704 {
705         centry_expand(centry, 4);
706         SIVAL(centry->data, centry->ofs, v);
707         centry->ofs += 4;
708 }
709
710 /*
711   push a uint16 into a centry 
712 */
713 static void centry_put_uint16(struct cache_entry *centry, uint16 v)
714 {
715         centry_expand(centry, 2);
716         SIVAL(centry->data, centry->ofs, v);
717         centry->ofs += 2;
718 }
719
720 /*
721   push a uint8 into a centry 
722 */
723 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
724 {
725         centry_expand(centry, 1);
726         SCVAL(centry->data, centry->ofs, v);
727         centry->ofs += 1;
728 }
729
730 /* 
731    push a string into a centry 
732  */
733 static void centry_put_string(struct cache_entry *centry, const char *s)
734 {
735         int len;
736
737         if (!s) {
738                 /* null strings are marked as len 0xFFFF */
739                 centry_put_uint8(centry, 0xFF);
740                 return;
741         }
742
743         len = strlen(s);
744         /* can't handle more than 254 char strings. Truncating is probably best */
745         if (len > 254) {
746                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
747                 len = 254;
748         }
749         centry_put_uint8(centry, len);
750         centry_expand(centry, len);
751         memcpy(centry->data + centry->ofs, s, len);
752         centry->ofs += len;
753 }
754
755 /* 
756    push a 16 byte hash into a centry - treat as 16 byte string.
757  */
758 static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
759 {
760         centry_put_uint8(centry, 16);
761         centry_expand(centry, 16);
762         memcpy(centry->data + centry->ofs, val, 16);
763         centry->ofs += 16;
764 }
765
766 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
767 {
768         fstring sid_string;
769         centry_put_string(centry, sid_to_fstring(sid_string, sid));
770 }
771
772
773 /*
774   put NTSTATUS into a centry
775 */
776 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
777 {
778         uint32 status_value = NT_STATUS_V(status);
779         centry_put_uint32(centry, status_value);
780 }
781
782
783 /*
784   push a NTTIME into a centry 
785 */
786 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
787 {
788         centry_expand(centry, 8);
789         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
790         centry->ofs += 4;
791         SIVAL(centry->data, centry->ofs, nt >> 32);
792         centry->ofs += 4;
793 }
794
795 /*
796   push a time_t into a centry - use a 64 bit size.
797   NTTIME here is being used as a convenient 64-bit size.
798 */
799 static void centry_put_time(struct cache_entry *centry, time_t t)
800 {
801         NTTIME nt = (NTTIME)t;
802         centry_put_nttime(centry, nt);
803 }
804
805 /*
806   start a centry for output. When finished, call centry_end()
807 */
808 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
809 {
810         struct cache_entry *centry;
811
812         if (!wcache->tdb)
813                 return NULL;
814
815         centry = SMB_XMALLOC_P(struct cache_entry);
816
817         centry->len = 8192; /* reasonable default */
818         centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
819         centry->ofs = 0;
820         centry->sequence_number = domain->sequence_number;
821         centry_put_ntstatus(centry, status);
822         centry_put_uint32(centry, centry->sequence_number);
823         return centry;
824 }
825
826 /*
827   finish a centry and write it to the tdb
828 */
829 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
830 static void centry_end(struct cache_entry *centry, const char *format, ...)
831 {
832         va_list ap;
833         char *kstr;
834         TDB_DATA key, data;
835
836         if (!winbindd_use_cache()) {
837                 return;
838         }
839
840         va_start(ap, format);
841         smb_xvasprintf(&kstr, format, ap);
842         va_end(ap);
843
844         key = string_tdb_data(kstr);
845         data.dptr = centry->data;
846         data.dsize = centry->ofs;
847
848         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
849         free(kstr);
850 }
851
852 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
853                                     NTSTATUS status, const char *domain_name,
854                                     const char *name, const DOM_SID *sid, 
855                                     enum lsa_SidType type)
856 {
857         struct cache_entry *centry;
858         fstring uname;
859
860         centry = centry_start(domain, status);
861         if (!centry)
862                 return;
863         centry_put_uint32(centry, type);
864         centry_put_sid(centry, sid);
865         fstrcpy(uname, name);
866         strupper_m(uname);
867         centry_end(centry, "NS/%s/%s", domain_name, uname);
868         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
869                   uname, sid_string_dbg(sid), nt_errstr(status)));
870         centry_free(centry);
871 }
872
873 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
874                                     const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType type)
875 {
876         struct cache_entry *centry;
877         fstring sid_string;
878
879         centry = centry_start(domain, status);
880         if (!centry)
881                 return;
882
883         if (NT_STATUS_IS_OK(status)) {
884                 centry_put_uint32(centry, type);
885                 centry_put_string(centry, domain_name);
886                 centry_put_string(centry, name);
887         }
888
889         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
890         DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
891                   name, nt_errstr(status)));
892         centry_free(centry);
893 }
894
895
896 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
897 {
898         struct cache_entry *centry;
899         fstring sid_string;
900
901         if (is_null_sid(&info->user_sid)) {
902                 return;
903         }
904
905         centry = centry_start(domain, status);
906         if (!centry)
907                 return;
908         centry_put_string(centry, info->acct_name);
909         centry_put_string(centry, info->full_name);
910         centry_put_string(centry, info->homedir);
911         centry_put_string(centry, info->shell);
912         centry_put_uint32(centry, info->primary_gid);
913         centry_put_sid(centry, &info->user_sid);
914         centry_put_sid(centry, &info->group_sid);
915         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
916                                                   &info->user_sid));
917         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
918         centry_free(centry);
919 }
920
921 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
922                                        NTSTATUS status,
923                                        struct samr_DomInfo12 *lockout_policy)
924 {
925         struct cache_entry *centry;
926
927         centry = centry_start(domain, status);
928         if (!centry)
929                 return;
930
931         centry_put_nttime(centry, lockout_policy->lockout_duration);
932         centry_put_nttime(centry, lockout_policy->lockout_window);
933         centry_put_uint16(centry, lockout_policy->lockout_threshold);
934
935         centry_end(centry, "LOC_POL/%s", domain->name);
936
937         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
938
939         centry_free(centry);
940 }
941
942
943
944 static void wcache_save_password_policy(struct winbindd_domain *domain,
945                                         NTSTATUS status,
946                                         struct samr_DomInfo1 *policy)
947 {
948         struct cache_entry *centry;
949
950         centry = centry_start(domain, status);
951         if (!centry)
952                 return;
953
954         centry_put_uint16(centry, policy->min_password_length);
955         centry_put_uint16(centry, policy->password_history_length);
956         centry_put_uint32(centry, policy->password_properties);
957         centry_put_nttime(centry, policy->max_password_age);
958         centry_put_nttime(centry, policy->min_password_age);
959
960         centry_end(centry, "PWD_POL/%s", domain->name);
961
962         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
963
964         centry_free(centry);
965 }
966
967 /***************************************************************************
968  ***************************************************************************/
969
970 static void wcache_save_username_alias(struct winbindd_domain *domain,
971                                        NTSTATUS status,
972                                        const char *name, const char *alias)
973 {
974         struct cache_entry *centry;
975         fstring uname;
976
977         if ( (centry = centry_start(domain, status)) == NULL )
978                 return;
979
980         centry_put_string( centry, alias );
981
982         fstrcpy(uname, name);
983         strupper_m(uname);
984         centry_end(centry, "NSS/NA/%s", uname);
985
986         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
987
988         centry_free(centry);
989 }
990
991 static void wcache_save_alias_username(struct winbindd_domain *domain,
992                                        NTSTATUS status,
993                                        const char *alias, const char *name)
994 {
995         struct cache_entry *centry;
996         fstring uname;
997
998         if ( (centry = centry_start(domain, status)) == NULL )
999                 return;
1000
1001         centry_put_string( centry, name );
1002
1003         fstrcpy(uname, alias);
1004         strupper_m(uname);
1005         centry_end(centry, "NSS/AN/%s", uname);
1006
1007         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1008
1009         centry_free(centry);
1010 }
1011
1012 /***************************************************************************
1013  ***************************************************************************/
1014
1015 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1016                                     struct winbindd_domain *domain,
1017                                     const char *name, char **alias )
1018 {
1019         struct winbind_cache *cache = get_cache(domain);
1020         struct cache_entry *centry = NULL;
1021         NTSTATUS status;
1022         char *upper_name;
1023
1024         if ( domain->internal )
1025                 return NT_STATUS_NOT_SUPPORTED;
1026
1027         if (!cache->tdb)
1028                 goto do_query;
1029
1030         if ( (upper_name = SMB_STRDUP(name)) == NULL )
1031                 return NT_STATUS_NO_MEMORY;
1032         strupper_m(upper_name);
1033
1034         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1035
1036         SAFE_FREE( upper_name );
1037
1038         if (!centry)
1039                 goto do_query;
1040
1041         status = centry->status;
1042
1043         if (!NT_STATUS_IS_OK(status)) {
1044                 centry_free(centry);
1045                 return status;
1046         }
1047
1048         *alias = centry_string( centry, mem_ctx );
1049
1050         centry_free(centry);
1051
1052         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1053                   name, *alias ? *alias : "(none)"));
1054
1055         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1056
1057 do_query:
1058
1059         /* If its not in cache and we are offline, then fail */
1060
1061         if ( get_global_winbindd_state_offline() || !domain->online ) {
1062                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1063                          "in offline mode\n"));
1064                 return NT_STATUS_NOT_FOUND;
1065         }
1066
1067         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1068
1069         if ( NT_STATUS_IS_OK( status ) ) {
1070                 wcache_save_username_alias(domain, status, name, *alias);
1071         }
1072
1073         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1074                 wcache_save_username_alias(domain, status, name, "(NULL)");
1075         }
1076
1077         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1078                  nt_errstr(status)));
1079
1080         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1081                 set_domain_offline( domain );
1082         }
1083
1084         return status;
1085 }
1086
1087 /***************************************************************************
1088  ***************************************************************************/
1089
1090 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1091                                     struct winbindd_domain *domain,
1092                                     const char *alias, char **name )
1093 {
1094         struct winbind_cache *cache = get_cache(domain);
1095         struct cache_entry *centry = NULL;
1096         NTSTATUS status;
1097         char *upper_name;
1098
1099         if ( domain->internal )
1100                 return  NT_STATUS_NOT_SUPPORTED;
1101
1102         if (!cache->tdb)
1103                 goto do_query;
1104
1105         if ( (upper_name = SMB_STRDUP(alias)) == NULL )
1106                 return NT_STATUS_NO_MEMORY;
1107         strupper_m(upper_name);
1108
1109         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1110
1111         SAFE_FREE( upper_name );
1112
1113         if (!centry)
1114                 goto do_query;
1115
1116         status = centry->status;
1117
1118         if (!NT_STATUS_IS_OK(status)) {
1119                 centry_free(centry);
1120                 return status;
1121         }
1122
1123         *name = centry_string( centry, mem_ctx );
1124
1125         centry_free(centry);
1126
1127         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1128                   alias, *name ? *name : "(none)"));
1129
1130         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1131
1132 do_query:
1133
1134         /* If its not in cache and we are offline, then fail */
1135
1136         if ( get_global_winbindd_state_offline() || !domain->online ) {
1137                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1138                          "in offline mode\n"));
1139                 return NT_STATUS_NOT_FOUND;
1140         }
1141
1142         /* an alias cannot contain a domain prefix or '@' */
1143
1144         if (strchr(alias, '\\') || strchr(alias, '@')) {
1145                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1146                           "qualified name %s\n", alias));
1147                 return NT_STATUS_OBJECT_NAME_INVALID;
1148         }
1149
1150         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1151
1152         if ( NT_STATUS_IS_OK( status ) ) {
1153                 wcache_save_alias_username( domain, status, alias, *name );
1154         }
1155
1156         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1157                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1158         }
1159
1160         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1161                  nt_errstr(status)));
1162
1163         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1164                 set_domain_offline( domain );
1165         }
1166
1167         return status;
1168 }
1169
1170 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
1171 {
1172         struct winbind_cache *cache = get_cache(domain);
1173         TDB_DATA data;
1174         fstring key_str, tmp;
1175         uint32 rid;
1176
1177         if (!cache->tdb) {
1178                 return NT_STATUS_INTERNAL_DB_ERROR;
1179         }
1180
1181         if (is_null_sid(sid)) {
1182                 return NT_STATUS_INVALID_SID;
1183         }
1184
1185         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1186                 return NT_STATUS_INVALID_SID;
1187         }
1188
1189         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1190
1191         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1192         if (!data.dptr) {
1193                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1194         }
1195
1196         SAFE_FREE(data.dptr);
1197         return NT_STATUS_OK;
1198 }
1199
1200 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1201    as new salted ones. */
1202
1203 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1204                           TALLOC_CTX *mem_ctx, 
1205                           const DOM_SID *sid,
1206                           const uint8 **cached_nt_pass,
1207                           const uint8 **cached_salt)
1208 {
1209         struct winbind_cache *cache = get_cache(domain);
1210         struct cache_entry *centry = NULL;
1211         NTSTATUS status;
1212         time_t t;
1213         uint32 rid;
1214         fstring tmp;
1215
1216         if (!cache->tdb) {
1217                 return NT_STATUS_INTERNAL_DB_ERROR;
1218         }
1219
1220         if (is_null_sid(sid)) {
1221                 return NT_STATUS_INVALID_SID;
1222         }
1223
1224         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1225                 return NT_STATUS_INVALID_SID;
1226         }
1227
1228         /* Try and get a salted cred first. If we can't
1229            fall back to an unsalted cred. */
1230
1231         centry = wcache_fetch(cache, domain, "CRED/%s",
1232                               sid_to_fstring(tmp, sid));
1233         if (!centry) {
1234                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1235                           sid_string_dbg(sid)));
1236                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1237         }
1238
1239         t = centry_time(centry);
1240
1241         /* In the salted case this isn't actually the nt_hash itself,
1242            but the MD5 of the salt + nt_hash. Let the caller
1243            sort this out. It can tell as we only return the cached_salt
1244            if we are returning a salted cred. */
1245
1246         *cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
1247         if (*cached_nt_pass == NULL) {
1248                 fstring sidstr;
1249
1250                 sid_to_fstring(sidstr, sid);
1251
1252                 /* Bad (old) cred cache. Delete and pretend we
1253                    don't have it. */
1254                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1255                                 sidstr));
1256                 wcache_delete("CRED/%s", sidstr);
1257                 centry_free(centry);
1258                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1259         }
1260
1261         /* We only have 17 bytes more data in the salted cred case. */
1262         if (centry->len - centry->ofs == 17) {
1263                 *cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
1264         } else {
1265                 *cached_salt = NULL;
1266         }
1267
1268         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1269         if (*cached_salt) {
1270                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1271         }
1272
1273         status = centry->status;
1274
1275         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1276                   sid_string_dbg(sid), nt_errstr(status) ));
1277
1278         centry_free(centry);
1279         return status;
1280 }
1281
1282 /* Store creds for a SID - only writes out new salted ones. */
1283
1284 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1285                            TALLOC_CTX *mem_ctx, 
1286                            const DOM_SID *sid, 
1287                            const uint8 nt_pass[NT_HASH_LEN])
1288 {
1289         struct cache_entry *centry;
1290         fstring sid_string;
1291         uint32 rid;
1292         uint8 cred_salt[NT_HASH_LEN];
1293         uint8 salted_hash[NT_HASH_LEN];
1294
1295         if (is_null_sid(sid)) {
1296                 return NT_STATUS_INVALID_SID;
1297         }
1298
1299         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1300                 return NT_STATUS_INVALID_SID;
1301         }
1302
1303         centry = centry_start(domain, NT_STATUS_OK);
1304         if (!centry) {
1305                 return NT_STATUS_INTERNAL_DB_ERROR;
1306         }
1307
1308         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1309
1310         centry_put_time(centry, time(NULL));
1311
1312         /* Create a salt and then salt the hash. */
1313         generate_random_buffer(cred_salt, NT_HASH_LEN);
1314         E_md5hash(cred_salt, nt_pass, salted_hash);
1315
1316         centry_put_hash16(centry, salted_hash);
1317         centry_put_hash16(centry, cred_salt);
1318         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1319
1320         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1321
1322         centry_free(centry);
1323
1324         return NT_STATUS_OK;
1325 }
1326
1327
1328 /* Query display info. This is the basic user list fn */
1329 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1330                                 TALLOC_CTX *mem_ctx,
1331                                 uint32 *num_entries, 
1332                                 WINBIND_USERINFO **info)
1333 {
1334         struct winbind_cache *cache = get_cache(domain);
1335         struct cache_entry *centry = NULL;
1336         NTSTATUS status;
1337         unsigned int i, retry;
1338
1339         if (!cache->tdb)
1340                 goto do_query;
1341
1342         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1343         if (!centry)
1344                 goto do_query;
1345
1346         *num_entries = centry_uint32(centry);
1347         
1348         if (*num_entries == 0)
1349                 goto do_cached;
1350
1351         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
1352         if (! (*info)) {
1353                 smb_panic_fn("query_user_list out of memory");
1354         }
1355         for (i=0; i<(*num_entries); i++) {
1356                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1357                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1358                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1359                 (*info)[i].shell = centry_string(centry, mem_ctx);
1360                 centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
1361                 centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
1362         }
1363
1364 do_cached:      
1365         status = centry->status;
1366
1367         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1368                 domain->name, nt_errstr(status) ));
1369
1370         centry_free(centry);
1371         return status;
1372
1373 do_query:
1374         *num_entries = 0;
1375         *info = NULL;
1376
1377         /* Return status value returned by seq number check */
1378
1379         if (!NT_STATUS_IS_OK(domain->last_status))
1380                 return domain->last_status;
1381
1382         /* Put the query_user_list() in a retry loop.  There appears to be
1383          * some bug either with Windows 2000 or Samba's handling of large
1384          * rpc replies.  This manifests itself as sudden disconnection
1385          * at a random point in the enumeration of a large (60k) user list.
1386          * The retry loop simply tries the operation again. )-:  It's not
1387          * pretty but an acceptable workaround until we work out what the
1388          * real problem is. */
1389
1390         retry = 0;
1391         do {
1392
1393                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1394                         domain->name ));
1395
1396                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1397                 if (!NT_STATUS_IS_OK(status)) {
1398                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1399                                   "retrying\n", NT_STATUS_V(status)));
1400                 }
1401                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1402                         DEBUG(3, ("query_user_list: flushing "
1403                                   "connection cache\n"));
1404                         invalidate_cm_connection(&domain->conn);
1405                 }
1406
1407         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1408                  (retry++ < 5));
1409
1410         /* and save it */
1411         refresh_sequence_number(domain, false);
1412         centry = centry_start(domain, status);
1413         if (!centry)
1414                 goto skip_save;
1415         centry_put_uint32(centry, *num_entries);
1416         for (i=0; i<(*num_entries); i++) {
1417                 centry_put_string(centry, (*info)[i].acct_name);
1418                 centry_put_string(centry, (*info)[i].full_name);
1419                 centry_put_string(centry, (*info)[i].homedir);
1420                 centry_put_string(centry, (*info)[i].shell);
1421                 centry_put_sid(centry, &(*info)[i].user_sid);
1422                 centry_put_sid(centry, &(*info)[i].group_sid);
1423                 if (domain->backend && domain->backend->consistent) {
1424                         /* when the backend is consistent we can pre-prime some mappings */
1425                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1426                                                 domain->name,
1427                                                 (*info)[i].acct_name, 
1428                                                 &(*info)[i].user_sid,
1429                                                 SID_NAME_USER);
1430                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1431                                                 &(*info)[i].user_sid,
1432                                                 domain->name,
1433                                                 (*info)[i].acct_name, 
1434                                                 SID_NAME_USER);
1435                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1436                 }
1437         }       
1438         centry_end(centry, "UL/%s", domain->name);
1439         centry_free(centry);
1440
1441 skip_save:
1442         return status;
1443 }
1444
1445 /* list all domain groups */
1446 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1447                                 TALLOC_CTX *mem_ctx,
1448                                 uint32 *num_entries, 
1449                                 struct acct_info **info)
1450 {
1451         struct winbind_cache *cache = get_cache(domain);
1452         struct cache_entry *centry = NULL;
1453         NTSTATUS status;
1454         unsigned int i;
1455
1456         if (!cache->tdb)
1457                 goto do_query;
1458
1459         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1460         if (!centry)
1461                 goto do_query;
1462
1463         *num_entries = centry_uint32(centry);
1464         
1465         if (*num_entries == 0)
1466                 goto do_cached;
1467
1468         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1469         if (! (*info)) {
1470                 smb_panic_fn("enum_dom_groups out of memory");
1471         }
1472         for (i=0; i<(*num_entries); i++) {
1473                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1474                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1475                 (*info)[i].rid = centry_uint32(centry);
1476         }
1477
1478 do_cached:      
1479         status = centry->status;
1480
1481         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1482                 domain->name, nt_errstr(status) ));
1483
1484         centry_free(centry);
1485         return status;
1486
1487 do_query:
1488         *num_entries = 0;
1489         *info = NULL;
1490
1491         /* Return status value returned by seq number check */
1492
1493         if (!NT_STATUS_IS_OK(domain->last_status))
1494                 return domain->last_status;
1495
1496         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1497                 domain->name ));
1498
1499         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1500
1501         /* and save it */
1502         refresh_sequence_number(domain, false);
1503         centry = centry_start(domain, status);
1504         if (!centry)
1505                 goto skip_save;
1506         centry_put_uint32(centry, *num_entries);
1507         for (i=0; i<(*num_entries); i++) {
1508                 centry_put_string(centry, (*info)[i].acct_name);
1509                 centry_put_string(centry, (*info)[i].acct_desc);
1510                 centry_put_uint32(centry, (*info)[i].rid);
1511         }       
1512         centry_end(centry, "GL/%s/domain", domain->name);
1513         centry_free(centry);
1514
1515 skip_save:
1516         return status;
1517 }
1518
1519 /* list all domain groups */
1520 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1521                                 TALLOC_CTX *mem_ctx,
1522                                 uint32 *num_entries, 
1523                                 struct acct_info **info)
1524 {
1525         struct winbind_cache *cache = get_cache(domain);
1526         struct cache_entry *centry = NULL;
1527         NTSTATUS status;
1528         unsigned int i;
1529
1530         if (!cache->tdb)
1531                 goto do_query;
1532
1533         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1534         if (!centry)
1535                 goto do_query;
1536
1537         *num_entries = centry_uint32(centry);
1538         
1539         if (*num_entries == 0)
1540                 goto do_cached;
1541
1542         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
1543         if (! (*info)) {
1544                 smb_panic_fn("enum_dom_groups out of memory");
1545         }
1546         for (i=0; i<(*num_entries); i++) {
1547                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1548                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1549                 (*info)[i].rid = centry_uint32(centry);
1550         }
1551
1552 do_cached:      
1553
1554         /* If we are returning cached data and the domain controller
1555            is down then we don't know whether the data is up to date
1556            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1557            indicate this. */
1558
1559         if (wcache_server_down(domain)) {
1560                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1561                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1562         } else
1563                 status = centry->status;
1564
1565         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1566                 domain->name, nt_errstr(status) ));
1567
1568         centry_free(centry);
1569         return status;
1570
1571 do_query:
1572         *num_entries = 0;
1573         *info = NULL;
1574
1575         /* Return status value returned by seq number check */
1576
1577         if (!NT_STATUS_IS_OK(domain->last_status))
1578                 return domain->last_status;
1579
1580         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1581                 domain->name ));
1582
1583         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1584
1585         /* and save it */
1586         refresh_sequence_number(domain, false);
1587         centry = centry_start(domain, status);
1588         if (!centry)
1589                 goto skip_save;
1590         centry_put_uint32(centry, *num_entries);
1591         for (i=0; i<(*num_entries); i++) {
1592                 centry_put_string(centry, (*info)[i].acct_name);
1593                 centry_put_string(centry, (*info)[i].acct_desc);
1594                 centry_put_uint32(centry, (*info)[i].rid);
1595         }
1596         centry_end(centry, "GL/%s/local", domain->name);
1597         centry_free(centry);
1598
1599 skip_save:
1600         return status;
1601 }
1602
1603 /* convert a single name to a sid in a domain */
1604 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1605                             TALLOC_CTX *mem_ctx,
1606                             enum winbindd_cmd orig_cmd,
1607                             const char *domain_name,
1608                             const char *name,
1609                             DOM_SID *sid,
1610                             enum lsa_SidType *type)
1611 {
1612         struct winbind_cache *cache = get_cache(domain);
1613         struct cache_entry *centry = NULL;
1614         NTSTATUS status;
1615         fstring uname;
1616
1617         if (!cache->tdb)
1618                 goto do_query;
1619
1620         fstrcpy(uname, name);
1621         strupper_m(uname);
1622         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1623         if (!centry)
1624                 goto do_query;
1625
1626         status = centry->status;
1627         if (NT_STATUS_IS_OK(status)) {
1628                 *type = (enum lsa_SidType)centry_uint32(centry);
1629                 centry_sid(centry, mem_ctx, sid);
1630         }
1631
1632         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
1633                 domain->name, nt_errstr(status) ));
1634
1635         centry_free(centry);
1636         return status;
1637
1638 do_query:
1639         ZERO_STRUCTP(sid);
1640
1641         /* If the seq number check indicated that there is a problem
1642          * with this DC, then return that status... except for
1643          * access_denied.  This is special because the dc may be in
1644          * "restrict anonymous = 1" mode, in which case it will deny
1645          * most unauthenticated operations, but *will* allow the LSA
1646          * name-to-sid that we try as a fallback. */
1647
1648         if (!(NT_STATUS_IS_OK(domain->last_status)
1649               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1650                 return domain->last_status;
1651
1652         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1653                 domain->name ));
1654
1655         status = domain->backend->name_to_sid(domain, mem_ctx, orig_cmd, 
1656                                               domain_name, name, sid, type);
1657
1658         /* and save it */
1659         refresh_sequence_number(domain, false);
1660
1661         if (domain->online &&
1662             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1663                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1664
1665                 /* Only save the reverse mapping if this was not a UPN */
1666                 if (!strchr(name, '@')) {
1667                         strupper_m(CONST_DISCARD(char *,domain_name));
1668                         strlower_m(CONST_DISCARD(char *,name));
1669                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1670                 }
1671         }
1672         
1673         return status;
1674 }
1675
1676 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1677    given */
1678 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1679                             TALLOC_CTX *mem_ctx,
1680                             const DOM_SID *sid,
1681                             char **domain_name,
1682                             char **name,
1683                             enum lsa_SidType *type)
1684 {
1685         struct winbind_cache *cache = get_cache(domain);
1686         struct cache_entry *centry = NULL;
1687         NTSTATUS status;
1688         fstring sid_string;
1689
1690         if (!cache->tdb)
1691                 goto do_query;
1692
1693         centry = wcache_fetch(cache, domain, "SN/%s",
1694                               sid_to_fstring(sid_string, sid));
1695         if (!centry)
1696                 goto do_query;
1697
1698         status = centry->status;
1699         if (NT_STATUS_IS_OK(status)) {
1700                 *type = (enum lsa_SidType)centry_uint32(centry);
1701                 *domain_name = centry_string(centry, mem_ctx);
1702                 *name = centry_string(centry, mem_ctx);
1703         }
1704
1705         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
1706                 domain->name, nt_errstr(status) ));
1707
1708         centry_free(centry);
1709         return status;
1710
1711 do_query:
1712         *name = NULL;
1713         *domain_name = NULL;
1714
1715         /* If the seq number check indicated that there is a problem
1716          * with this DC, then return that status... except for
1717          * access_denied.  This is special because the dc may be in
1718          * "restrict anonymous = 1" mode, in which case it will deny
1719          * most unauthenticated operations, but *will* allow the LSA
1720          * sid-to-name that we try as a fallback. */
1721
1722         if (!(NT_STATUS_IS_OK(domain->last_status)
1723               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1724                 return domain->last_status;
1725
1726         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1727                 domain->name ));
1728
1729         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1730
1731         /* and save it */
1732         refresh_sequence_number(domain, false);
1733         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1734
1735         /* We can't save the name to sid mapping here, as with sid history a
1736          * later name2sid would give the wrong sid. */
1737
1738         return status;
1739 }
1740
1741 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
1742                               TALLOC_CTX *mem_ctx,
1743                               const DOM_SID *domain_sid,
1744                               uint32 *rids,
1745                               size_t num_rids,
1746                               char **domain_name,
1747                               char ***names,
1748                               enum lsa_SidType **types)
1749 {
1750         struct winbind_cache *cache = get_cache(domain);
1751         size_t i;
1752         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1753         bool have_mapped;
1754         bool have_unmapped;
1755
1756         *domain_name = NULL;
1757         *names = NULL;
1758         *types = NULL;
1759
1760         if (!cache->tdb) {
1761                 goto do_query;
1762         }
1763
1764         if (num_rids == 0) {
1765                 return NT_STATUS_OK;
1766         }
1767
1768         *names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
1769         *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
1770
1771         if ((*names == NULL) || (*types == NULL)) {
1772                 result = NT_STATUS_NO_MEMORY;
1773                 goto error;
1774         }
1775
1776         have_mapped = have_unmapped = false;
1777
1778         for (i=0; i<num_rids; i++) {
1779                 DOM_SID sid;
1780                 struct cache_entry *centry;
1781                 fstring tmp;
1782
1783                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1784                         result = NT_STATUS_INTERNAL_ERROR;
1785                         goto error;
1786                 }
1787
1788                 centry = wcache_fetch(cache, domain, "SN/%s",
1789                                       sid_to_fstring(tmp, &sid));
1790                 if (!centry) {
1791                         goto do_query;
1792                 }
1793
1794                 (*types)[i] = SID_NAME_UNKNOWN;
1795                 (*names)[i] = talloc_strdup(*names, "");
1796
1797                 if (NT_STATUS_IS_OK(centry->status)) {
1798                         char *dom;
1799                         have_mapped = true;
1800                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
1801
1802                         dom = centry_string(centry, mem_ctx);
1803                         if (*domain_name == NULL) {
1804                                 *domain_name = dom;
1805                         } else {
1806                                 talloc_free(dom);
1807                         }
1808
1809                         (*names)[i] = centry_string(centry, *names);
1810
1811                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
1812                            || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
1813                         have_unmapped = true;
1814
1815                 } else {
1816                         /* something's definitely wrong */
1817                         result = centry->status;
1818                         goto error;
1819                 }
1820
1821                 centry_free(centry);
1822         }
1823
1824         if (!have_mapped) {
1825                 return NT_STATUS_NONE_MAPPED;
1826         }
1827         if (!have_unmapped) {
1828                 return NT_STATUS_OK;
1829         }
1830         return STATUS_SOME_UNMAPPED;
1831
1832  do_query:
1833
1834         TALLOC_FREE(*names);
1835         TALLOC_FREE(*types);
1836
1837         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
1838                                                 rids, num_rids, domain_name,
1839                                                 names, types);
1840
1841         /*
1842           None of the queried rids has been found so save all negative entries
1843         */
1844         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
1845                 for (i = 0; i < num_rids; i++) {
1846                         DOM_SID sid;
1847                         const char *name = "";
1848                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
1849                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
1850                         
1851                         if (!sid_compose(&sid, domain_sid, rids[i])) {
1852                                 return NT_STATUS_INTERNAL_ERROR;
1853                         }
1854
1855                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1856                                                 name, type);
1857                 }
1858
1859                 return result;
1860         }
1861
1862         /*
1863           Some or all of the queried rids have been found.
1864         */
1865         if (!NT_STATUS_IS_OK(result) &&
1866             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
1867                 return result;
1868         }
1869
1870         refresh_sequence_number(domain, false);
1871
1872         for (i=0; i<num_rids; i++) {
1873                 DOM_SID sid;
1874                 NTSTATUS status;
1875
1876                 if (!sid_compose(&sid, domain_sid, rids[i])) {
1877                         result = NT_STATUS_INTERNAL_ERROR;
1878                         goto error;
1879                 }
1880
1881                 status = (*types)[i] == SID_NAME_UNKNOWN ?
1882                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
1883
1884                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
1885                                         (*names)[i], (*types)[i]);
1886         }
1887
1888         return result;
1889
1890  error:
1891         
1892         TALLOC_FREE(*names);
1893         TALLOC_FREE(*types);
1894         return result;
1895 }
1896
1897 /* Lookup user information from a rid */
1898 static NTSTATUS query_user(struct winbindd_domain *domain, 
1899                            TALLOC_CTX *mem_ctx, 
1900                            const DOM_SID *user_sid, 
1901                            WINBIND_USERINFO *info)
1902 {
1903         struct winbind_cache *cache = get_cache(domain);
1904         struct cache_entry *centry = NULL;
1905         NTSTATUS status;
1906         fstring tmp;
1907
1908         if (!cache->tdb)
1909                 goto do_query;
1910
1911         centry = wcache_fetch(cache, domain, "U/%s",
1912                               sid_to_fstring(tmp, user_sid));
1913         
1914         /* If we have an access denied cache entry and a cached info3 in the
1915            samlogon cache then do a query.  This will force the rpc back end
1916            to return the info3 data. */
1917
1918         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1919             netsamlogon_cache_have(user_sid)) {
1920                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1921                 domain->last_status = NT_STATUS_OK;
1922                 centry_free(centry);
1923                 goto do_query;
1924         }
1925         
1926         if (!centry)
1927                 goto do_query;
1928         
1929         /* if status is not ok then this is a negative hit
1930            and the rest of the data doesn't matter */
1931         status = centry->status;
1932         if (NT_STATUS_IS_OK(status)) {
1933                 info->acct_name = centry_string(centry, mem_ctx);
1934                 info->full_name = centry_string(centry, mem_ctx);
1935                 info->homedir = centry_string(centry, mem_ctx);
1936                 info->shell = centry_string(centry, mem_ctx);
1937                 info->primary_gid = centry_uint32(centry);
1938                 centry_sid(centry, mem_ctx, &info->user_sid);
1939                 centry_sid(centry, mem_ctx, &info->group_sid);
1940         }
1941
1942         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
1943                 domain->name, nt_errstr(status) ));
1944
1945         centry_free(centry);
1946         return status;
1947
1948 do_query:
1949         ZERO_STRUCTP(info);
1950
1951         /* Return status value returned by seq number check */
1952
1953         if (!NT_STATUS_IS_OK(domain->last_status))
1954                 return domain->last_status;
1955         
1956         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
1957                 domain->name ));
1958
1959         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1960
1961         /* and save it */
1962         refresh_sequence_number(domain, false);
1963         wcache_save_user(domain, status, info);
1964
1965         return status;
1966 }
1967
1968
1969 /* Lookup groups a user is a member of. */
1970 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1971                                   TALLOC_CTX *mem_ctx,
1972                                   const DOM_SID *user_sid, 
1973                                   uint32 *num_groups, DOM_SID **user_gids)
1974 {
1975         struct winbind_cache *cache = get_cache(domain);
1976         struct cache_entry *centry = NULL;
1977         NTSTATUS status;
1978         unsigned int i;
1979         fstring sid_string;
1980
1981         if (!cache->tdb)
1982                 goto do_query;
1983
1984         centry = wcache_fetch(cache, domain, "UG/%s",
1985                               sid_to_fstring(sid_string, user_sid));
1986         
1987         /* If we have an access denied cache entry and a cached info3 in the
1988            samlogon cache then do a query.  This will force the rpc back end
1989            to return the info3 data. */
1990
1991         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1992             netsamlogon_cache_have(user_sid)) {
1993                 DEBUG(10, ("lookup_usergroups: cached access denied and have cached info3\n"));
1994                 domain->last_status = NT_STATUS_OK;
1995                 centry_free(centry);
1996                 goto do_query;
1997         }
1998         
1999         if (!centry)
2000                 goto do_query;
2001
2002         *num_groups = centry_uint32(centry);
2003         
2004         if (*num_groups == 0)
2005                 goto do_cached;
2006
2007         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
2008         if (! (*user_gids)) {
2009                 smb_panic_fn("lookup_usergroups out of memory");
2010         }
2011         for (i=0; i<(*num_groups); i++) {
2012                 centry_sid(centry, mem_ctx, &(*user_gids)[i]);
2013         }
2014
2015 do_cached:      
2016         status = centry->status;
2017
2018         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
2019                 domain->name, nt_errstr(status) ));
2020
2021         centry_free(centry);
2022         return status;
2023
2024 do_query:
2025         (*num_groups) = 0;
2026         (*user_gids) = NULL;
2027
2028         /* Return status value returned by seq number check */
2029
2030         if (!NT_STATUS_IS_OK(domain->last_status))
2031                 return domain->last_status;
2032
2033         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2034                 domain->name ));
2035
2036         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2037
2038         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2039                 goto skip_save;
2040         
2041         /* and save it */
2042         refresh_sequence_number(domain, false);
2043         centry = centry_start(domain, status);
2044         if (!centry)
2045                 goto skip_save;
2046
2047         centry_put_uint32(centry, *num_groups);
2048         for (i=0; i<(*num_groups); i++) {
2049                 centry_put_sid(centry, &(*user_gids)[i]);
2050         }       
2051
2052         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2053         centry_free(centry);
2054
2055 skip_save:
2056         return status;
2057 }
2058
2059 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2060                                    TALLOC_CTX *mem_ctx,
2061                                    uint32 num_sids, const DOM_SID *sids,
2062                                    uint32 *num_aliases, uint32 **alias_rids)
2063 {
2064         struct winbind_cache *cache = get_cache(domain);
2065         struct cache_entry *centry = NULL;
2066         NTSTATUS status;
2067         char *sidlist = talloc_strdup(mem_ctx, "");
2068         int i;
2069
2070         if (!cache->tdb)
2071                 goto do_query;
2072
2073         if (num_sids == 0) {
2074                 *num_aliases = 0;
2075                 *alias_rids = NULL;
2076                 return NT_STATUS_OK;
2077         }
2078
2079         /* We need to cache indexed by the whole list of SIDs, the aliases
2080          * resulting might come from any of the SIDs. */
2081
2082         for (i=0; i<num_sids; i++) {
2083                 fstring tmp;
2084                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
2085                                           sid_to_fstring(tmp, &sids[i]));
2086                 if (sidlist == NULL)
2087                         return NT_STATUS_NO_MEMORY;
2088         }
2089
2090         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2091
2092         if (!centry)
2093                 goto do_query;
2094
2095         *num_aliases = centry_uint32(centry);
2096         *alias_rids = NULL;
2097
2098         if (*num_aliases) {
2099                 (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
2100
2101                 if ((*alias_rids) == NULL) {
2102                         centry_free(centry);
2103                         return NT_STATUS_NO_MEMORY;
2104                 }
2105         } else {
2106                 (*alias_rids) = NULL;
2107         }
2108
2109         for (i=0; i<(*num_aliases); i++)
2110                 (*alias_rids)[i] = centry_uint32(centry);
2111
2112         status = centry->status;
2113
2114         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2115                   "status %s\n", domain->name, nt_errstr(status)));
2116
2117         centry_free(centry);
2118         return status;
2119
2120  do_query:
2121         (*num_aliases) = 0;
2122         (*alias_rids) = NULL;
2123
2124         if (!NT_STATUS_IS_OK(domain->last_status))
2125                 return domain->last_status;
2126
2127         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2128                   "for domain %s\n", domain->name ));
2129
2130         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2131                                                      num_sids, sids,
2132                                                      num_aliases, alias_rids);
2133
2134         /* and save it */
2135         refresh_sequence_number(domain, false);
2136         centry = centry_start(domain, status);
2137         if (!centry)
2138                 goto skip_save;
2139         centry_put_uint32(centry, *num_aliases);
2140         for (i=0; i<(*num_aliases); i++)
2141                 centry_put_uint32(centry, (*alias_rids)[i]);
2142         centry_end(centry, "UA%s", sidlist);
2143         centry_free(centry);
2144
2145  skip_save:
2146         return status;
2147 }
2148
2149
2150 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2151                                 TALLOC_CTX *mem_ctx,
2152                                 const DOM_SID *group_sid, uint32 *num_names, 
2153                                 DOM_SID **sid_mem, char ***names, 
2154                                 uint32 **name_types)
2155 {
2156         struct winbind_cache *cache = get_cache(domain);
2157         struct cache_entry *centry = NULL;
2158         NTSTATUS status;
2159         unsigned int i;
2160         fstring sid_string;
2161
2162         if (!cache->tdb)
2163                 goto do_query;
2164
2165         centry = wcache_fetch(cache, domain, "GM/%s",
2166                               sid_to_fstring(sid_string, group_sid));
2167         if (!centry)
2168                 goto do_query;
2169
2170         *num_names = centry_uint32(centry);
2171         
2172         if (*num_names == 0)
2173                 goto do_cached;
2174
2175         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
2176         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
2177         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
2178
2179         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
2180                 smb_panic_fn("lookup_groupmem out of memory");
2181         }
2182
2183         for (i=0; i<(*num_names); i++) {
2184                 centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
2185                 (*names)[i] = centry_string(centry, mem_ctx);
2186                 (*name_types)[i] = centry_uint32(centry);
2187         }
2188
2189 do_cached:      
2190         status = centry->status;
2191
2192         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status: %s\n",
2193                 domain->name, nt_errstr(status)));
2194
2195         centry_free(centry);
2196         return status;
2197
2198 do_query:
2199         (*num_names) = 0;
2200         (*sid_mem) = NULL;
2201         (*names) = NULL;
2202         (*name_types) = NULL;
2203         
2204         /* Return status value returned by seq number check */
2205
2206         if (!NT_STATUS_IS_OK(domain->last_status))
2207                 return domain->last_status;
2208
2209         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2210                 domain->name ));
2211
2212         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
2213                                                   sid_mem, names, name_types);
2214
2215         /* and save it */
2216         refresh_sequence_number(domain, false);
2217         centry = centry_start(domain, status);
2218         if (!centry)
2219                 goto skip_save;
2220         centry_put_uint32(centry, *num_names);
2221         for (i=0; i<(*num_names); i++) {
2222                 centry_put_sid(centry, &(*sid_mem)[i]);
2223                 centry_put_string(centry, (*names)[i]);
2224                 centry_put_uint32(centry, (*name_types)[i]);
2225         }       
2226         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2227         centry_free(centry);
2228
2229 skip_save:
2230         return status;
2231 }
2232
2233 /* find the sequence number for a domain */
2234 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
2235 {
2236         refresh_sequence_number(domain, false);
2237
2238         *seq = domain->sequence_number;
2239
2240         return NT_STATUS_OK;
2241 }
2242
2243 /* enumerate trusted domains 
2244  * (we need to have the list of trustdoms in the cache when we go offline) -
2245  * Guenther */
2246 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2247                                 TALLOC_CTX *mem_ctx,
2248                                 uint32 *num_domains,
2249                                 char ***names,
2250                                 char ***alt_names,
2251                                 DOM_SID **dom_sids)
2252 {
2253         struct winbind_cache *cache = get_cache(domain);
2254         struct cache_entry *centry = NULL;
2255         NTSTATUS status;
2256         int i;
2257  
2258         if (!cache->tdb)
2259                 goto do_query;
2260  
2261         centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
2262         
2263         if (!centry) {
2264                 goto do_query;
2265         }
2266  
2267         *num_domains = centry_uint32(centry);
2268         
2269         if (*num_domains) {
2270                 (*names)        = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2271                 (*alt_names)    = TALLOC_ARRAY(mem_ctx, char *, *num_domains);
2272                 (*dom_sids)     = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
2273  
2274                 if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
2275                         smb_panic_fn("trusted_domains out of memory");
2276                 }
2277         } else {
2278                 (*names) = NULL;
2279                 (*alt_names) = NULL;
2280                 (*dom_sids) = NULL;
2281         }
2282  
2283         for (i=0; i<(*num_domains); i++) {
2284                 (*names)[i] = centry_string(centry, mem_ctx);
2285                 (*alt_names)[i] = centry_string(centry, mem_ctx);
2286                 if (!centry_sid(centry, mem_ctx, &(*dom_sids)[i])) {
2287                         sid_copy(&(*dom_sids)[i], &global_sid_NULL);
2288                 }
2289         }
2290
2291         status = centry->status;
2292  
2293         DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
2294                 domain->name, *num_domains, nt_errstr(status) ));
2295  
2296         centry_free(centry);
2297         return status;
2298  
2299 do_query:
2300         (*num_domains) = 0;
2301         (*dom_sids) = NULL;
2302         (*names) = NULL;
2303         (*alt_names) = NULL;
2304  
2305         /* Return status value returned by seq number check */
2306
2307         if (!NT_STATUS_IS_OK(domain->last_status))
2308                 return domain->last_status;
2309         
2310         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2311                 domain->name ));
2312  
2313         status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
2314                                                 names, alt_names, dom_sids);
2315
2316         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2317          * so that the generic centry handling still applies correctly -
2318          * Guenther*/
2319
2320         if (!NT_STATUS_IS_ERR(status)) {
2321                 status = NT_STATUS_OK;
2322         }
2323
2324
2325 #if 0    /* Disabled as we want the trust dom list to be managed by
2326             the main parent and always to make the query.  --jerry */
2327
2328         /* and save it */
2329         refresh_sequence_number(domain, false);
2330  
2331         centry = centry_start(domain, status);
2332         if (!centry)
2333                 goto skip_save;
2334
2335         centry_put_uint32(centry, *num_domains);
2336
2337         for (i=0; i<(*num_domains); i++) {
2338                 centry_put_string(centry, (*names)[i]);
2339                 centry_put_string(centry, (*alt_names)[i]);
2340                 centry_put_sid(centry, &(*dom_sids)[i]);
2341         }
2342         
2343         centry_end(centry, "TRUSTDOMS/%s", domain->name);
2344  
2345         centry_free(centry);
2346  
2347 skip_save:
2348 #endif
2349
2350         return status;
2351 }       
2352
2353 /* get lockout policy */
2354 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2355                                TALLOC_CTX *mem_ctx,
2356                                struct samr_DomInfo12 *policy)
2357 {
2358         struct winbind_cache *cache = get_cache(domain);
2359         struct cache_entry *centry = NULL;
2360         NTSTATUS status;
2361
2362         if (!cache->tdb)
2363                 goto do_query;
2364
2365         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2366
2367         if (!centry)
2368                 goto do_query;
2369
2370         policy->lockout_duration = centry_nttime(centry);
2371         policy->lockout_window = centry_nttime(centry);
2372         policy->lockout_threshold = centry_uint16(centry);
2373
2374         status = centry->status;
2375
2376         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2377                 domain->name, nt_errstr(status) ));
2378
2379         centry_free(centry);
2380         return status;
2381
2382 do_query:
2383         ZERO_STRUCTP(policy);
2384
2385         /* Return status value returned by seq number check */
2386
2387         if (!NT_STATUS_IS_OK(domain->last_status))
2388                 return domain->last_status;
2389
2390         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2391                 domain->name ));
2392
2393         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2394
2395         /* and save it */
2396         refresh_sequence_number(domain, false);
2397         wcache_save_lockout_policy(domain, status, policy);
2398
2399         return status;
2400 }
2401
2402 /* get password policy */
2403 static NTSTATUS password_policy(struct winbindd_domain *domain,
2404                                 TALLOC_CTX *mem_ctx,
2405                                 struct samr_DomInfo1 *policy)
2406 {
2407         struct winbind_cache *cache = get_cache(domain);
2408         struct cache_entry *centry = NULL;
2409         NTSTATUS status;
2410
2411         if (!cache->tdb)
2412                 goto do_query;
2413
2414         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
2415
2416         if (!centry)
2417                 goto do_query;
2418
2419         policy->min_password_length = centry_uint16(centry);
2420         policy->password_history_length = centry_uint16(centry);
2421         policy->password_properties = centry_uint32(centry);
2422         policy->max_password_age = centry_nttime(centry);
2423         policy->min_password_age = centry_nttime(centry);
2424
2425         status = centry->status;
2426
2427         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2428                 domain->name, nt_errstr(status) ));
2429
2430         centry_free(centry);
2431         return status;
2432
2433 do_query:
2434         ZERO_STRUCTP(policy);
2435
2436         /* Return status value returned by seq number check */
2437
2438         if (!NT_STATUS_IS_OK(domain->last_status))
2439                 return domain->last_status;
2440
2441         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
2442                 domain->name ));
2443
2444         status = domain->backend->password_policy(domain, mem_ctx, policy);
2445
2446         /* and save it */
2447         refresh_sequence_number(domain, false);
2448         if (NT_STATUS_IS_OK(status)) {
2449                 wcache_save_password_policy(domain, status, policy);
2450         }
2451
2452         return status;
2453 }
2454
2455
2456 /* Invalidate cached user and group lists coherently */
2457
2458 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2459                        void *state)
2460 {
2461         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
2462             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
2463                 tdb_delete(the_tdb, kbuf);
2464
2465         return 0;
2466 }
2467
2468 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
2469
2470 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
2471                                 struct netr_SamInfo3 *info3)
2472 {
2473         DOM_SID sid;
2474         fstring key_str, sid_string;
2475         struct winbind_cache *cache;
2476
2477         /* dont clear cached U/SID and UG/SID entries when we want to logon
2478          * offline - gd */
2479
2480         if (lp_winbind_offline_logon()) {
2481                 return;
2482         }
2483
2484         if (!domain)
2485                 return;
2486
2487         cache = get_cache(domain);
2488
2489         if (!cache->tdb) {
2490                 return;
2491         }
2492
2493         sid_copy(&sid, info3->base.domain_sid);
2494         sid_append_rid(&sid, info3->base.rid);
2495
2496         /* Clear U/SID cache entry */
2497         fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid));
2498         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2499         tdb_delete(cache->tdb, string_tdb_data(key_str));
2500
2501         /* Clear UG/SID cache entry */
2502         fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid));
2503         DEBUG(10, ("wcache_invalidate_samlogon: clearing %s\n", key_str));
2504         tdb_delete(cache->tdb, string_tdb_data(key_str));
2505
2506         /* Samba/winbindd never needs this. */
2507         netsamlogon_clear_cached_user(info3);
2508 }
2509
2510 bool wcache_invalidate_cache(void)
2511 {
2512         struct winbindd_domain *domain;
2513
2514         for (domain = domain_list(); domain; domain = domain->next) {
2515                 struct winbind_cache *cache = get_cache(domain);
2516
2517                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
2518                            "entries for %s\n", domain->name));
2519                 if (cache) {
2520                         if (cache->tdb) {
2521                                 tdb_traverse(cache->tdb, traverse_fn, NULL);
2522                         } else {
2523                                 return false;
2524                         }
2525                 }
2526         }
2527         return true;
2528 }
2529
2530 bool init_wcache(void)
2531 {
2532         if (wcache == NULL) {
2533                 wcache = SMB_XMALLOC_P(struct winbind_cache);
2534                 ZERO_STRUCTP(wcache);
2535         }
2536
2537         if (wcache->tdb != NULL)
2538                 return true;
2539
2540         /* when working offline we must not clear the cache on restart */
2541         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2542                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2543                                 ( lp_winbind_offline_logon()
2544                                   ? TDB_DEFAULT
2545                                   : TDB_DEFAULT | TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ),
2546                                 O_RDWR|O_CREAT, 0600);
2547
2548         if (wcache->tdb == NULL) {
2549                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2550                 return false;
2551         }
2552
2553         return true;
2554 }
2555
2556 /************************************************************************
2557  This is called by the parent to initialize the cache file.
2558  We don't need sophisticated locking here as we know we're the
2559  only opener.
2560 ************************************************************************/
2561
2562 bool initialize_winbindd_cache(void)
2563 {
2564         bool cache_bad = true;
2565         uint32 vers;
2566
2567         if (!init_wcache()) {
2568                 DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
2569                 return false;
2570         }
2571
2572         /* Check version number. */
2573         if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
2574                         vers == WINBINDD_CACHE_VERSION) {
2575                 cache_bad = false;
2576         }
2577
2578         if (cache_bad) {
2579                 DEBUG(0,("initialize_winbindd_cache: clearing cache "
2580                         "and re-creating with version number %d\n",
2581                         WINBINDD_CACHE_VERSION ));
2582
2583                 tdb_close(wcache->tdb);
2584                 wcache->tdb = NULL;
2585
2586                 if (unlink(cache_path("winbindd_cache.tdb")) == -1) {
2587                         DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
2588                                 cache_path("winbindd_cache.tdb"),
2589                                 strerror(errno) ));
2590                         return false;
2591                 }
2592                 if (!init_wcache()) {
2593                         DEBUG(0,("initialize_winbindd_cache: re-initialization "
2594                                         "init_wcache failed.\n"));
2595                         return false;
2596                 }
2597
2598                 /* Write the version. */
2599                 if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
2600                         DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
2601                                 tdb_errorstr(wcache->tdb) ));
2602                         return false;
2603                 }
2604         }
2605
2606         tdb_close(wcache->tdb);
2607         wcache->tdb = NULL;
2608         return true;
2609 }
2610
2611 void close_winbindd_cache(void)
2612 {
2613         if (!wcache) {
2614                 return;
2615         }
2616         if (wcache->tdb) {
2617                 tdb_close(wcache->tdb);
2618                 wcache->tdb = NULL;
2619         }
2620 }
2621
2622 void cache_store_response(pid_t pid, struct winbindd_response *response)
2623 {
2624         fstring key_str;
2625
2626         if (!init_wcache())
2627                 return;
2628
2629         DEBUG(10, ("Storing response for pid %d, len %d\n",
2630                    (int)pid, response->length));
2631
2632         fstr_sprintf(key_str, "DR/%d", (int)pid);
2633         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
2634                       make_tdb_data((uint8 *)response, sizeof(*response)),
2635                       TDB_REPLACE) == -1)
2636                 return;
2637
2638         if (response->length == sizeof(*response))
2639                 return;
2640
2641         /* There's extra data */
2642
2643         DEBUG(10, ("Storing extra data: len=%d\n",
2644                    (int)(response->length - sizeof(*response))));
2645
2646         fstr_sprintf(key_str, "DE/%d", (int)pid);
2647         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
2648                       make_tdb_data((uint8 *)response->extra_data.data,
2649                                     response->length - sizeof(*response)),
2650                       TDB_REPLACE) == 0)
2651                 return;
2652
2653         /* We could not store the extra data, make sure the tdb does not
2654          * contain a main record with wrong dangling extra data */
2655
2656         fstr_sprintf(key_str, "DR/%d", (int)pid);
2657         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2658
2659         return;
2660 }
2661
2662 bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
2663 {
2664         TDB_DATA data;
2665         fstring key_str;
2666
2667         if (!init_wcache())
2668                 return false;
2669
2670         DEBUG(10, ("Retrieving response for pid %d\n", (int)pid));
2671
2672         fstr_sprintf(key_str, "DR/%d", (int)pid);
2673         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2674
2675         if (data.dptr == NULL)
2676                 return false;
2677
2678         if (data.dsize != sizeof(*response))
2679                 return false;
2680
2681         memcpy(response, data.dptr, data.dsize);
2682         SAFE_FREE(data.dptr);
2683
2684         if (response->length == sizeof(*response)) {
2685                 response->extra_data.data = NULL;
2686                 return true;
2687         }
2688
2689         /* There's extra data */
2690
2691         DEBUG(10, ("Retrieving extra data length=%d\n",
2692                    (int)(response->length - sizeof(*response))));
2693
2694         fstr_sprintf(key_str, "DE/%d", (int)pid);
2695         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
2696
2697         if (data.dptr == NULL) {
2698                 DEBUG(0, ("Did not find extra data\n"));
2699                 return false;
2700         }
2701
2702         if (data.dsize != (response->length - sizeof(*response))) {
2703                 DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
2704                 SAFE_FREE(data.dptr);
2705                 return false;
2706         }
2707
2708         dump_data(11, (uint8 *)data.dptr, data.dsize);
2709
2710         response->extra_data.data = data.dptr;
2711         return true;
2712 }
2713
2714 void cache_cleanup_response(pid_t pid)
2715 {
2716         fstring key_str;
2717
2718         if (!init_wcache())
2719                 return;
2720
2721         fstr_sprintf(key_str, "DR/%d", (int)pid);
2722         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2723
2724         fstr_sprintf(key_str, "DE/%d", (int)pid);
2725         tdb_delete(wcache->tdb, string_tdb_data(key_str));
2726
2727         return;
2728 }
2729
2730
2731 bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
2732                        char **domain_name, char **name,
2733                        enum lsa_SidType *type)
2734 {
2735         struct winbindd_domain *domain;
2736         struct winbind_cache *cache;
2737         struct cache_entry *centry = NULL;
2738         NTSTATUS status;
2739         fstring tmp;
2740
2741         domain = find_lookup_domain_from_sid(sid);
2742         if (domain == NULL) {
2743                 return false;
2744         }
2745
2746         cache = get_cache(domain);
2747
2748         if (cache->tdb == NULL) {
2749                 return false;
2750         }
2751
2752         centry = wcache_fetch(cache, domain, "SN/%s",
2753                               sid_to_fstring(tmp, sid));
2754         if (centry == NULL) {
2755                 return false;
2756         }
2757
2758         if (NT_STATUS_IS_OK(centry->status)) {
2759                 *type = (enum lsa_SidType)centry_uint32(centry);
2760                 *domain_name = centry_string(centry, mem_ctx);
2761                 *name = centry_string(centry, mem_ctx);
2762         }
2763
2764         status = centry->status;
2765         centry_free(centry);
2766         return NT_STATUS_IS_OK(status);
2767 }
2768
2769 bool lookup_cached_name(TALLOC_CTX *mem_ctx,
2770                         const char *domain_name,
2771                         const char *name,
2772                         DOM_SID *sid,
2773                         enum lsa_SidType *type)
2774 {
2775         struct winbindd_domain *domain;
2776         struct winbind_cache *cache;
2777         struct cache_entry *centry = NULL;
2778         NTSTATUS status;
2779         fstring uname;
2780         bool original_online_state;     
2781
2782         domain = find_lookup_domain_from_name(domain_name);
2783         if (domain == NULL) {
2784                 return false;
2785         }
2786
2787         cache = get_cache(domain);
2788
2789         if (cache->tdb == NULL) {
2790                 return false;
2791         }
2792
2793         fstrcpy(uname, name);
2794         strupper_m(uname);
2795         
2796         /* If we are doing a cached logon, temporarily set the domain
2797            offline so the cache won't expire the entry */
2798         
2799         original_online_state = domain->online;
2800         domain->online = false;
2801         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
2802         domain->online = original_online_state;
2803         
2804         if (centry == NULL) {
2805                 return false;
2806         }
2807
2808         if (NT_STATUS_IS_OK(centry->status)) {
2809                 *type = (enum lsa_SidType)centry_uint32(centry);
2810                 centry_sid(centry, mem_ctx, sid);
2811         }
2812
2813         status = centry->status;
2814         centry_free(centry);
2815         
2816         return NT_STATUS_IS_OK(status);
2817 }
2818
2819 void cache_name2sid(struct winbindd_domain *domain, 
2820                     const char *domain_name, const char *name,
2821                     enum lsa_SidType type, const DOM_SID *sid)
2822 {
2823         refresh_sequence_number(domain, false);
2824         wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
2825                                 sid, type);
2826 }
2827
2828 /*
2829  * The original idea that this cache only contains centries has
2830  * been blurred - now other stuff gets put in here. Ensure we
2831  * ignore these things on cleanup.
2832  */
2833
2834 static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
2835                                TDB_DATA dbuf, void *state)
2836 {
2837         struct cache_entry *centry;
2838
2839         if (is_non_centry_key(kbuf)) {
2840                 return 0;
2841         }
2842
2843         centry = wcache_fetch_raw((char *)kbuf.dptr);
2844         if (!centry) {
2845                 return 0;
2846         }
2847
2848         if (!NT_STATUS_IS_OK(centry->status)) {
2849                 DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
2850                 tdb_delete(the_tdb, kbuf);
2851         }
2852
2853         centry_free(centry);
2854         return 0;
2855 }
2856
2857 /* flush the cache */
2858 void wcache_flush_cache(void)
2859 {
2860         if (!wcache)
2861                 return;
2862         if (wcache->tdb) {
2863                 tdb_close(wcache->tdb);
2864                 wcache->tdb = NULL;
2865         }
2866         if (!winbindd_use_cache()) {
2867                 return;
2868         }
2869
2870         /* when working offline we must not clear the cache on restart */
2871         wcache->tdb = tdb_open_log(cache_path("winbindd_cache.tdb"),
2872                                 WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
2873                                 ( lp_winbind_offline_logon()
2874                                   ? TDB_DEFAULT
2875                                   : TDB_DEFAULT | TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ),
2876                                 O_RDWR|O_CREAT, 0600);
2877
2878         if (!wcache->tdb) {
2879                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
2880                 return;
2881         }
2882
2883         tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
2884
2885         DEBUG(10,("wcache_flush_cache success\n"));
2886 }
2887
2888 /* Count cached creds */
2889
2890 static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2891                                     void *state)
2892 {
2893         int *cred_count = (int*)state;
2894  
2895         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2896                 (*cred_count)++;
2897         }
2898         return 0;
2899 }
2900
2901 NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
2902 {
2903         struct winbind_cache *cache = get_cache(domain);
2904
2905         *count = 0;
2906
2907         if (!cache->tdb) {
2908                 return NT_STATUS_INTERNAL_DB_ERROR;
2909         }
2910  
2911         tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
2912
2913         return NT_STATUS_OK;
2914 }
2915
2916 struct cred_list {
2917         struct cred_list *prev, *next;
2918         TDB_DATA key;
2919         fstring name;
2920         time_t created;
2921 };
2922 static struct cred_list *wcache_cred_list;
2923
2924 static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
2925                                     void *state)
2926 {
2927         struct cred_list *cred;
2928
2929         if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
2930
2931                 cred = SMB_MALLOC_P(struct cred_list);
2932                 if (cred == NULL) {
2933                         DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
2934                         return -1;
2935                 }
2936
2937                 ZERO_STRUCTP(cred);
2938                 
2939                 /* save a copy of the key */
2940                 
2941                 fstrcpy(cred->name, (const char *)kbuf.dptr);           
2942                 DLIST_ADD(wcache_cred_list, cred);
2943         }
2944         
2945         return 0;
2946 }
2947
2948 NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
2949 {
2950         struct winbind_cache *cache = get_cache(domain);
2951         NTSTATUS status;
2952         int ret;
2953         struct cred_list *cred, *oldest = NULL;
2954
2955         if (!cache->tdb) {
2956                 return NT_STATUS_INTERNAL_DB_ERROR;
2957         }
2958
2959         /* we possibly already have an entry */
2960         if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
2961         
2962                 fstring key_str, tmp;
2963
2964                 DEBUG(11,("we already have an entry, deleting that\n"));
2965
2966                 fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
2967
2968                 tdb_delete(cache->tdb, string_tdb_data(key_str));
2969
2970                 return NT_STATUS_OK;
2971         }
2972
2973         ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
2974         if (ret == 0) {
2975                 return NT_STATUS_OK;
2976         } else if ((ret == -1) || (wcache_cred_list == NULL)) {
2977                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
2978         }
2979
2980         ZERO_STRUCTP(oldest);
2981
2982         for (cred = wcache_cred_list; cred; cred = cred->next) {
2983
2984                 TDB_DATA data;
2985                 time_t t;
2986
2987                 data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
2988                 if (!data.dptr) {
2989                         DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
2990                                 cred->name));
2991                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
2992                         goto done;
2993                 }
2994         
2995                 t = IVAL(data.dptr, 0);
2996                 SAFE_FREE(data.dptr);
2997
2998                 if (!oldest) {
2999                         oldest = SMB_MALLOC_P(struct cred_list);
3000                         if (oldest == NULL) {
3001                                 status = NT_STATUS_NO_MEMORY;
3002                                 goto done;
3003                         }
3004
3005                         fstrcpy(oldest->name, cred->name);
3006                         oldest->created = t;
3007                         continue;
3008                 }
3009
3010                 if (t < oldest->created) {
3011                         fstrcpy(oldest->name, cred->name);
3012                         oldest->created = t;
3013                 }
3014         }
3015
3016         if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
3017                 status = NT_STATUS_OK;
3018         } else {
3019                 status = NT_STATUS_UNSUCCESSFUL;
3020         }
3021 done:
3022         SAFE_FREE(wcache_cred_list);
3023         SAFE_FREE(oldest);
3024         
3025         return status;
3026 }
3027
3028 /* Change the global online/offline state. */
3029 bool set_global_winbindd_state_offline(void)
3030 {
3031         TDB_DATA data;
3032
3033         DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
3034
3035         /* Only go offline if someone has created
3036            the key "WINBINDD_OFFLINE" in the cache tdb. */
3037
3038         if (wcache == NULL || wcache->tdb == NULL) {
3039                 DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
3040                 return false;
3041         }
3042
3043         if (!lp_winbind_offline_logon()) {
3044                 DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
3045                 return false;
3046         }
3047
3048         if (global_winbindd_offline_state) {
3049                 /* Already offline. */
3050                 return true;
3051         }
3052
3053         data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
3054
3055         if (!data.dptr || data.dsize != 4) {
3056                 DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
3057                 SAFE_FREE(data.dptr);
3058                 return false;
3059         } else {
3060                 DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
3061                 global_winbindd_offline_state = true;
3062                 SAFE_FREE(data.dptr);
3063                 return true;
3064         }
3065 }
3066
3067 void set_global_winbindd_state_online(void)
3068 {
3069         DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
3070
3071         if (!lp_winbind_offline_logon()) {
3072                 DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
3073                 return;
3074         }
3075
3076         if (!global_winbindd_offline_state) {
3077                 /* Already online. */
3078                 return;
3079         }
3080         global_winbindd_offline_state = false;
3081
3082         if (!wcache->tdb) {
3083                 return;
3084         }
3085
3086         /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
3087         tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
3088 }
3089
3090 bool get_global_winbindd_state_offline(void)
3091 {
3092         return global_winbindd_offline_state;
3093 }
3094
3095 /***********************************************************************
3096  Validate functions for all possible cache tdb keys.
3097 ***********************************************************************/
3098
3099 static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
3100                                                   struct tdb_validation_status *state)
3101 {
3102         struct cache_entry *centry;
3103
3104         centry = SMB_XMALLOC_P(struct cache_entry);
3105         centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
3106         if (!centry->data) {
3107                 SAFE_FREE(centry);
3108                 return NULL;
3109         }
3110         centry->len = data.dsize;
3111         centry->ofs = 0;
3112
3113         if (centry->len < 8) {
3114                 /* huh? corrupt cache? */
3115                 DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
3116                 centry_free(centry);
3117                 state->bad_entry = true;
3118                 state->success = false;
3119                 return NULL;
3120         }
3121
3122         centry->status = NT_STATUS(centry_uint32(centry));
3123         centry->sequence_number = centry_uint32(centry);
3124         return centry;
3125 }
3126
3127 static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3128                            struct tdb_validation_status *state)
3129 {
3130         if (dbuf.dsize != 8) {
3131                 DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
3132                                 keystr, (unsigned int)dbuf.dsize ));
3133                 state->bad_entry = true;
3134                 return 1;
3135         }
3136         return 0;
3137 }
3138
3139 static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3140                        struct tdb_validation_status *state)
3141 {
3142         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3143         if (!centry) {
3144                 return 1;
3145         }
3146
3147         (void)centry_uint32(centry);
3148         if (NT_STATUS_IS_OK(centry->status)) {
3149                 DOM_SID sid;
3150                 (void)centry_sid(centry, mem_ctx, &sid);
3151         }
3152
3153         centry_free(centry);
3154
3155         if (!(state->success)) {
3156                 return 1;
3157         }
3158         DEBUG(10,("validate_ns: %s ok\n", keystr));
3159         return 0;
3160 }
3161
3162 static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3163                        struct tdb_validation_status *state)
3164 {
3165         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3166         if (!centry) {
3167                 return 1;
3168         }
3169
3170         if (NT_STATUS_IS_OK(centry->status)) {
3171                 (void)centry_uint32(centry);
3172                 (void)centry_string(centry, mem_ctx);
3173                 (void)centry_string(centry, mem_ctx);
3174         }
3175
3176         centry_free(centry);
3177
3178         if (!(state->success)) {
3179                 return 1;
3180         }
3181         DEBUG(10,("validate_sn: %s ok\n", keystr));
3182         return 0;
3183 }
3184
3185 static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3186                       struct tdb_validation_status *state)
3187 {
3188         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3189         DOM_SID sid;
3190
3191         if (!centry) {
3192                 return 1;
3193         }
3194
3195         (void)centry_string(centry, mem_ctx);
3196         (void)centry_string(centry, mem_ctx);
3197         (void)centry_string(centry, mem_ctx);
3198         (void)centry_string(centry, mem_ctx);
3199         (void)centry_uint32(centry);
3200         (void)centry_sid(centry, mem_ctx, &sid);
3201         (void)centry_sid(centry, mem_ctx, &sid);
3202
3203         centry_free(centry);
3204
3205         if (!(state->success)) {
3206                 return 1;
3207         }
3208         DEBUG(10,("validate_u: %s ok\n", keystr));
3209         return 0;
3210 }
3211
3212 static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3213                             struct tdb_validation_status *state)
3214 {
3215         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3216
3217         if (!centry) {
3218                 return 1;
3219         }
3220
3221         (void)centry_nttime(centry);
3222         (void)centry_nttime(centry);
3223         (void)centry_uint16(centry);
3224
3225         centry_free(centry);
3226
3227         if (!(state->success)) {
3228                 return 1;
3229         }
3230         DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
3231         return 0;
3232 }
3233
3234 static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3235                             struct tdb_validation_status *state)
3236 {
3237         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3238
3239         if (!centry) {
3240                 return 1;
3241         }
3242
3243         (void)centry_uint16(centry);
3244         (void)centry_uint16(centry);
3245         (void)centry_uint32(centry);
3246         (void)centry_nttime(centry);
3247         (void)centry_nttime(centry);
3248
3249         centry_free(centry);
3250
3251         if (!(state->success)) {
3252                 return 1;
3253         }
3254         DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
3255         return 0;
3256 }
3257
3258 static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3259                          struct tdb_validation_status *state)
3260 {
3261         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3262
3263         if (!centry) {
3264                 return 1;
3265         }
3266
3267         (void)centry_time(centry);
3268         (void)centry_hash16(centry, mem_ctx);
3269
3270         /* We only have 17 bytes more data in the salted cred case. */
3271         if (centry->len - centry->ofs == 17) {
3272                 (void)centry_hash16(centry, mem_ctx);
3273         }
3274
3275         centry_free(centry);
3276
3277         if (!(state->success)) {
3278                 return 1;
3279         }
3280         DEBUG(10,("validate_cred: %s ok\n", keystr));
3281         return 0;
3282 }
3283
3284 static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3285                        struct tdb_validation_status *state)
3286 {
3287         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3288         int32 num_entries, i;
3289
3290         if (!centry) {
3291                 return 1;
3292         }
3293
3294         num_entries = (int32)centry_uint32(centry);
3295
3296         for (i=0; i< num_entries; i++) {
3297                 DOM_SID sid;
3298                 (void)centry_string(centry, mem_ctx);
3299                 (void)centry_string(centry, mem_ctx);
3300                 (void)centry_string(centry, mem_ctx);
3301                 (void)centry_string(centry, mem_ctx);
3302                 (void)centry_sid(centry, mem_ctx, &sid);
3303                 (void)centry_sid(centry, mem_ctx, &sid);
3304         }
3305
3306         centry_free(centry);
3307
3308         if (!(state->success)) {
3309                 return 1;
3310         }
3311         DEBUG(10,("validate_ul: %s ok\n", keystr));
3312         return 0;
3313 }
3314
3315 static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3316                        struct tdb_validation_status *state)
3317 {
3318         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3319         int32 num_entries, i;
3320
3321         if (!centry) {
3322                 return 1;
3323         }
3324
3325         num_entries = centry_uint32(centry);
3326         
3327         for (i=0; i< num_entries; i++) {
3328                 (void)centry_string(centry, mem_ctx);
3329                 (void)centry_string(centry, mem_ctx);
3330                 (void)centry_uint32(centry);
3331         }
3332
3333         centry_free(centry);
3334
3335         if (!(state->success)) {
3336                 return 1;
3337         }
3338         DEBUG(10,("validate_gl: %s ok\n", keystr));
3339         return 0;
3340 }
3341
3342 static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3343                        struct tdb_validation_status *state)
3344 {
3345         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3346         int32 num_groups, i;
3347
3348         if (!centry) {
3349                 return 1;
3350         }
3351
3352         num_groups = centry_uint32(centry);
3353
3354         for (i=0; i< num_groups; i++) {
3355                 DOM_SID sid;
3356                 centry_sid(centry, mem_ctx, &sid);
3357         }
3358
3359         centry_free(centry);
3360
3361         if (!(state->success)) {
3362                 return 1;
3363         }
3364         DEBUG(10,("validate_ug: %s ok\n", keystr));
3365         return 0;
3366 }
3367
3368 static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3369                        struct tdb_validation_status *state)
3370 {
3371         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3372         int32 num_aliases, i;
3373
3374         if (!centry) {
3375                 return 1;
3376         }
3377
3378         num_aliases = centry_uint32(centry);
3379
3380         for (i=0; i < num_aliases; i++) {
3381                 (void)centry_uint32(centry);
3382         }
3383
3384         centry_free(centry);
3385
3386         if (!(state->success)) {
3387                 return 1;
3388         }
3389         DEBUG(10,("validate_ua: %s ok\n", keystr));
3390         return 0;
3391 }
3392
3393 static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3394                        struct tdb_validation_status *state)
3395 {
3396         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3397         int32 num_names, i;
3398
3399         if (!centry) {
3400                 return 1;
3401         }
3402
3403         num_names = centry_uint32(centry);
3404
3405         for (i=0; i< num_names; i++) {
3406                 DOM_SID sid;
3407                 centry_sid(centry, mem_ctx, &sid);
3408                 (void)centry_string(centry, mem_ctx);
3409                 (void)centry_uint32(centry);
3410         }
3411
3412         centry_free(centry);
3413
3414         if (!(state->success)) {
3415                 return 1;
3416         }
3417         DEBUG(10,("validate_gm: %s ok\n", keystr));
3418         return 0;
3419 }
3420
3421 static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3422                        struct tdb_validation_status *state)
3423 {
3424         /* Can't say anything about this other than must be nonzero. */
3425         if (dbuf.dsize == 0) {
3426                 DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
3427                                 keystr));
3428                 state->bad_entry = true;
3429                 state->success = false;
3430                 return 1;
3431         }
3432
3433         DEBUG(10,("validate_dr: %s ok\n", keystr));
3434         return 0;
3435 }
3436
3437 static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3438                        struct tdb_validation_status *state)
3439 {
3440         /* Can't say anything about this other than must be nonzero. */
3441         if (dbuf.dsize == 0) {
3442                 DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
3443                                 keystr));
3444                 state->bad_entry = true;
3445                 state->success = false;
3446                 return 1;
3447         }
3448
3449         DEBUG(10,("validate_de: %s ok\n", keystr));
3450         return 0;
3451 }
3452
3453 static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
3454                            TDB_DATA dbuf, struct tdb_validation_status *state)
3455 {
3456         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3457
3458         if (!centry) {
3459                 return 1;
3460         }
3461
3462         (void)centry_string(centry, mem_ctx);
3463         (void)centry_string(centry, mem_ctx);
3464         (void)centry_string(centry, mem_ctx);
3465         (void)centry_uint32(centry);
3466
3467         centry_free(centry);
3468
3469         if (!(state->success)) {
3470                 return 1;
3471         }
3472         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3473         return 0;
3474 }
3475
3476 static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
3477                            TDB_DATA dbuf,
3478                            struct tdb_validation_status *state)
3479 {
3480         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3481
3482         if (!centry) {
3483                 return 1;
3484         }
3485
3486         (void)centry_string( centry, mem_ctx );
3487
3488         centry_free(centry);
3489
3490         if (!(state->success)) {
3491                 return 1;
3492         }
3493         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3494         return 0;
3495 }
3496
3497 static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
3498                            TDB_DATA dbuf,
3499                            struct tdb_validation_status *state)
3500 {
3501         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3502
3503         if (!centry) {
3504                 return 1;
3505         }
3506
3507         (void)centry_string( centry, mem_ctx );
3508
3509         centry_free(centry);
3510
3511         if (!(state->success)) {
3512                 return 1;
3513         }
3514         DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
3515         return 0;
3516 }
3517
3518 static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3519                               struct tdb_validation_status *state)
3520 {
3521         struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
3522         int32 num_domains, i;
3523
3524         if (!centry) {
3525                 return 1;
3526         }
3527
3528         num_domains = centry_uint32(centry);
3529         
3530         for (i=0; i< num_domains; i++) {
3531                 DOM_SID sid;
3532                 (void)centry_string(centry, mem_ctx);
3533                 (void)centry_string(centry, mem_ctx);
3534                 (void)centry_sid(centry, mem_ctx, &sid);
3535         }
3536
3537         centry_free(centry);
3538
3539         if (!(state->success)) {
3540                 return 1;
3541         }
3542         DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
3543         return 0;
3544 }
3545
3546 static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
3547                                   TDB_DATA dbuf,
3548                                   struct tdb_validation_status *state)
3549 {
3550         if (dbuf.dsize == 0) {
3551                 DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
3552                           "key %s (len ==0) ?\n", keystr));
3553                 state->bad_entry = true;
3554                 state->success = false;
3555                 return 1;
3556         }
3557
3558         DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
3559         DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
3560         return 0;
3561 }
3562
3563 static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3564                             struct tdb_validation_status *state)
3565 {
3566         if (dbuf.dsize != 4) {
3567                 DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
3568                                 keystr, (unsigned int)dbuf.dsize ));
3569                 state->bad_entry = true;
3570                 state->success = false;
3571                 return 1;
3572         }
3573         DEBUG(10,("validate_offline: %s ok\n", keystr));
3574         return 0;
3575 }
3576
3577 static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
3578                                   struct tdb_validation_status *state)
3579 {
3580         if (dbuf.dsize != 4) {
3581                 DEBUG(0, ("validate_cache_version: Corrupt cache for "
3582                           "key %s (len %u != 4) ?\n", 
3583                           keystr, (unsigned int)dbuf.dsize));
3584                 state->bad_entry = true;
3585                 state->success = false;
3586                 return 1;
3587         }
3588
3589         DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
3590         return 0;
3591 }
3592
3593 /***********************************************************************
3594  A list of all possible cache tdb keys with associated validation
3595  functions.
3596 ***********************************************************************/
3597
3598 struct key_val_struct {
3599         const char *keyname;
3600         int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
3601 } key_val[] = {
3602         {"SEQNUM/", validate_seqnum},
3603         {"NS/", validate_ns},
3604         {"SN/", validate_sn},
3605         {"U/", validate_u},
3606         {"LOC_POL/", validate_loc_pol},
3607         {"PWD_POL/", validate_pwd_pol},
3608         {"CRED/", validate_cred},
3609         {"UL/", validate_ul},
3610         {"GL/", validate_gl},
3611         {"UG/", validate_ug},
3612         {"UA", validate_ua},
3613         {"GM/", validate_gm},
3614         {"DR/", validate_dr},
3615         {"DE/", validate_de},
3616         {"NSS/PWINFO/", validate_pwinfo},
3617         {"TRUSTDOMS/", validate_trustdoms},
3618         {"TRUSTDOMCACHE/", validate_trustdomcache},
3619         {"NSS/NA/", validate_nss_na},
3620         {"NSS/AN/", validate_nss_an},
3621         {"WINBINDD_OFFLINE", validate_offline},
3622         {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
3623         {NULL, NULL}
3624 };
3625
3626 /***********************************************************************
3627  Function to look at every entry in the tdb and validate it as far as
3628  possible.
3629 ***********************************************************************/
3630
3631 static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
3632 {
3633         int i;
3634         unsigned int max_key_len = 1024;
3635         struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
3636
3637         /* Paranoia check. */
3638         if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
3639                 max_key_len = 1024 * 1024;
3640         }
3641         if (kbuf.dsize > max_key_len) {
3642                 DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
3643                           "(%u) > (%u)\n\n",
3644                           (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
3645                 return 1;
3646         }
3647
3648         for (i = 0; key_val[i].keyname; i++) {
3649                 size_t namelen = strlen(key_val[i].keyname);
3650                 if (kbuf.dsize >= namelen && (
3651                                 strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
3652                         TALLOC_CTX *mem_ctx;
3653                         char *keystr;
3654                         int ret;
3655
3656                         keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
3657                         if (!keystr) {
3658                                 return 1;
3659                         }
3660                         memcpy(keystr, kbuf.dptr, kbuf.dsize);
3661                         keystr[kbuf.dsize] = '\0';
3662
3663                         mem_ctx = talloc_init("validate_ctx");
3664                         if (!mem_ctx) {
3665                                 SAFE_FREE(keystr);
3666                                 return 1;
3667                         }
3668
3669                         ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
3670                                                           v_state);
3671
3672                         SAFE_FREE(keystr);
3673                         talloc_destroy(mem_ctx);
3674                         return ret;
3675                 }
3676         }
3677
3678         DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
3679         dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
3680         DEBUG(0,("data :\n"));
3681         dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
3682         v_state->unknown_key = true;
3683         v_state->success = false;
3684         return 1; /* terminate. */
3685 }
3686
3687 static void validate_panic(const char *const why)
3688 {
3689         DEBUG(0,("validating cache: would panic %s\n", why ));
3690         DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
3691         exit(47);
3692 }
3693
3694 /***********************************************************************
3695  Try and validate every entry in the winbindd cache. If we fail here,
3696  delete the cache tdb and return non-zero.
3697 ***********************************************************************/
3698
3699 int winbindd_validate_cache(void)
3700 {
3701         int ret = -1;
3702         const char *tdb_path = cache_path("winbindd_cache.tdb");
3703         TDB_CONTEXT *tdb = NULL;
3704
3705         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3706         smb_panic_fn = validate_panic;
3707
3708
3709         tdb = tdb_open_log(tdb_path, 
3710                            WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
3711                            ( lp_winbind_offline_logon() 
3712                              ? TDB_DEFAULT 
3713                              : TDB_DEFAULT | TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ),
3714                            O_RDWR|O_CREAT, 
3715                            0600);
3716         if (!tdb) {
3717                 DEBUG(0, ("winbindd_validate_cache: "
3718                           "error opening/initializing tdb\n"));
3719                 goto done;
3720         }
3721         tdb_close(tdb);
3722
3723         ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
3724
3725         if (ret != 0) {
3726                 DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
3727                 DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
3728                 unlink(tdb_path);
3729         }
3730
3731 done:
3732         DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
3733         smb_panic_fn = smb_panic;
3734         return ret;
3735 }
3736
3737 /***********************************************************************
3738  Try and validate every entry in the winbindd cache.
3739 ***********************************************************************/
3740
3741 int winbindd_validate_cache_nobackup(void)
3742 {
3743         int ret = -1;
3744         const char *tdb_path = cache_path("winbindd_cache.tdb");
3745
3746         DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
3747         smb_panic_fn = validate_panic;
3748
3749
3750         if (wcache == NULL || wcache->tdb == NULL) {
3751                 ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
3752         } else {
3753                 ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
3754         }
3755
3756         if (ret != 0) {
3757                 DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
3758                            "successful.\n"));
3759         }
3760
3761         DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
3762                    "function\n"));
3763         smb_panic_fn = smb_panic;
3764         return ret;
3765 }
3766
3767 bool winbindd_cache_validate_and_initialize(void)
3768 {
3769         close_winbindd_cache();
3770
3771         if (lp_winbind_offline_logon()) {
3772                 if (winbindd_validate_cache() < 0) {
3773                         DEBUG(0, ("winbindd cache tdb corrupt and no backup "
3774                                   "could be restored.\n"));
3775                 }
3776         }
3777
3778         return initialize_winbindd_cache();
3779 }
3780
3781 /*********************************************************************
3782  ********************************************************************/
3783
3784 static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
3785                                        struct winbindd_tdc_domain **domains, 
3786                                        size_t *num_domains )
3787 {
3788         struct winbindd_tdc_domain *list = NULL;
3789         size_t idx;
3790         int i;
3791         bool set_only = false;
3792         
3793         /* don't allow duplicates */
3794
3795         idx = *num_domains;
3796         list = *domains;
3797         
3798         for ( i=0; i< (*num_domains); i++ ) {
3799                 if ( strequal( new_dom->name, list[i].domain_name ) ) {
3800                         DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
3801                                   new_dom->name));
3802                         idx = i;
3803                         set_only = true;
3804                         
3805                         break;
3806                 }
3807         }
3808
3809         if ( !set_only ) {
3810                 if ( !*domains ) {
3811                         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
3812                         idx = 0;
3813                 } else {
3814                         list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
3815                                                      struct winbindd_tdc_domain,  
3816                                                      (*num_domains)+1);
3817                         idx = *num_domains;             
3818                 }
3819
3820                 ZERO_STRUCT( list[idx] );
3821         }
3822
3823         if ( !list )
3824                 return false;
3825
3826         list[idx].domain_name = talloc_strdup( list, new_dom->name );
3827         list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
3828
3829         if ( !is_null_sid( &new_dom->sid ) ) {
3830                 sid_copy( &list[idx].sid, &new_dom->sid );
3831         } else {
3832                 sid_copy(&list[idx].sid, &global_sid_NULL);
3833         }
3834
3835         if ( new_dom->domain_flags != 0x0 )
3836                 list[idx].trust_flags = new_dom->domain_flags;  
3837
3838         if ( new_dom->domain_type != 0x0 )
3839                 list[idx].trust_type = new_dom->domain_type;
3840
3841         if ( new_dom->domain_trust_attribs != 0x0 )
3842                 list[idx].trust_attribs = new_dom->domain_trust_attribs;
3843         
3844         if ( !set_only ) {
3845                 *domains = list;
3846                 *num_domains = idx + 1; 
3847         }
3848
3849         return true;
3850 }
3851
3852 /*********************************************************************
3853  ********************************************************************/
3854
3855 static TDB_DATA make_tdc_key( const char *domain_name )
3856 {
3857         char *keystr = NULL;
3858         TDB_DATA key = { NULL, 0 };
3859         
3860         if ( !domain_name ) {
3861                 DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
3862                 return key;
3863         }
3864                
3865                 
3866         if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
3867                 return key;
3868         }
3869         key = string_term_tdb_data(keystr);
3870         
3871         return key;     
3872 }
3873
3874 /*********************************************************************
3875  ********************************************************************/
3876
3877 static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
3878                              size_t num_domains,
3879                              unsigned char **buf )
3880 {
3881         unsigned char *buffer = NULL;
3882         int len = 0;
3883         int buflen = 0;
3884         int i = 0;
3885
3886         DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
3887                   (int)num_domains));
3888         
3889         buflen = 0;
3890         
3891  again: 
3892         len = 0;
3893         
3894         /* Store the number of array items first */
3895         len += tdb_pack( buffer+len, buflen-len, "d", 
3896                          num_domains );
3897
3898         /* now pack each domain trust record */
3899         for ( i=0; i<num_domains; i++ ) {
3900
3901                 fstring tmp;
3902
3903                 if ( buflen > 0 ) {
3904                         DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
3905                                   domains[i].domain_name,
3906                                   domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
3907                 }
3908                 
3909                 len += tdb_pack( buffer+len, buflen-len, "fffddd",
3910                                  domains[i].domain_name,
3911                                  domains[i].dns_name,
3912                                  sid_to_fstring(tmp, &domains[i].sid),
3913                                  domains[i].trust_flags,
3914                                  domains[i].trust_attribs,
3915                                  domains[i].trust_type );
3916         }
3917
3918         if ( buflen < len ) {
3919                 SAFE_FREE(buffer);
3920                 if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
3921                         DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
3922                         buflen = -1;
3923                         goto done;
3924                 }
3925                 buflen = len;
3926                 goto again;
3927         }
3928
3929         *buf = buffer;  
3930         
3931  done:  
3932         return buflen;  
3933 }
3934
3935 /*********************************************************************
3936  ********************************************************************/
3937
3938 static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
3939                                   struct winbindd_tdc_domain **domains )
3940 {
3941         fstring domain_name, dns_name, sid_string;      
3942         uint32 type, attribs, flags;
3943         int num_domains;
3944         int len = 0;
3945         int i;
3946         struct winbindd_tdc_domain *list = NULL;
3947
3948         /* get the number of domains */
3949         len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
3950         if ( len == -1 ) {
3951                 DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));               
3952                 return 0;
3953         }
3954
3955         list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
3956         if ( !list ) {
3957                 DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
3958                 return 0;               
3959         }
3960         
3961         for ( i=0; i<num_domains; i++ ) {
3962                 len += tdb_unpack( buf+len, buflen-len, "fffddd",
3963                                    domain_name,
3964                                    dns_name,
3965                                    sid_string,
3966                                    &flags,
3967                                    &attribs,
3968                                    &type );
3969
3970                 if ( len == -1 ) {
3971                         DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
3972                         TALLOC_FREE( list );                    
3973                         return 0;
3974                 }
3975
3976                 DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
3977                           "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
3978                           domain_name, dns_name, sid_string,
3979                           flags, attribs, type));
3980                 
3981                 list[i].domain_name = talloc_strdup( list, domain_name );
3982                 list[i].dns_name = talloc_strdup( list, dns_name );
3983                 if ( !string_to_sid( &(list[i].sid), sid_string ) ) {                   
3984                         DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
3985                                   domain_name));
3986                 }
3987                 list[i].trust_flags = flags;
3988                 list[i].trust_attribs = attribs;
3989                 list[i].trust_type = type;
3990         }
3991
3992         *domains = list;
3993         
3994         return num_domains;
3995 }
3996
3997 /*********************************************************************
3998  ********************************************************************/
3999
4000 static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
4001 {
4002         TDB_DATA key = make_tdc_key( lp_workgroup() );   
4003         TDB_DATA data = { NULL, 0 };
4004         int ret;
4005         
4006         if ( !key.dptr )
4007                 return false;
4008         
4009         /* See if we were asked to delete the cache entry */
4010
4011         if ( !domains ) {
4012                 ret = tdb_delete( wcache->tdb, key );
4013                 goto done;
4014         }
4015         
4016         data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
4017         
4018         if ( !data.dptr ) {
4019                 ret = -1;
4020                 goto done;
4021         }
4022                 
4023         ret = tdb_store( wcache->tdb, key, data, 0 );
4024
4025  done:
4026         SAFE_FREE( data.dptr );
4027         SAFE_FREE( key.dptr );
4028         
4029         return ( ret != -1 );   
4030 }
4031
4032 /*********************************************************************
4033  ********************************************************************/
4034
4035 bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
4036 {
4037         TDB_DATA key = make_tdc_key( lp_workgroup() );
4038         TDB_DATA data = { NULL, 0 };
4039
4040         *domains = NULL;        
4041         *num_domains = 0;       
4042
4043         if ( !key.dptr )
4044                 return false;
4045         
4046         data = tdb_fetch( wcache->tdb, key );
4047
4048         SAFE_FREE( key.dptr );
4049         
4050         if ( !data.dptr ) 
4051                 return false;
4052         
4053         *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
4054
4055         SAFE_FREE( data.dptr );
4056         
4057         if ( !*domains )
4058                 return false;
4059
4060         return true;
4061 }
4062
4063 /*********************************************************************
4064  ********************************************************************/
4065
4066 bool wcache_tdc_add_domain( struct winbindd_domain *domain )
4067 {
4068         struct winbindd_tdc_domain *dom_list = NULL;
4069         size_t num_domains = 0;
4070         bool ret = false;
4071
4072         DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
4073                   "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
4074                   domain->name, domain->alt_name, 
4075                   sid_string_dbg(&domain->sid),
4076                   domain->domain_flags,
4077                   domain->domain_trust_attribs,
4078                   domain->domain_type));        
4079         
4080         if ( !init_wcache() ) {
4081                 return false;
4082         }
4083         
4084         /* fetch the list */
4085
4086         wcache_tdc_fetch_list( &dom_list, &num_domains );
4087         
4088         /* add the new domain */
4089
4090         if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
4091                 goto done;              
4092         }       
4093
4094         /* pack the domain */
4095
4096         if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
4097                 goto done;              
4098         }
4099         
4100         /* Success */
4101
4102         ret = true;
4103  done:
4104         TALLOC_FREE( dom_list );
4105         
4106         return ret;     
4107 }
4108
4109 /*********************************************************************
4110  ********************************************************************/
4111
4112 struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
4113 {
4114         struct winbindd_tdc_domain *dom_list = NULL;
4115         size_t num_domains = 0;
4116         int i;
4117         struct winbindd_tdc_domain *d = NULL;   
4118
4119         DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
4120
4121         if ( !init_wcache() ) {
4122                 return false;
4123         }
4124         
4125         /* fetch the list */
4126
4127         wcache_tdc_fetch_list( &dom_list, &num_domains );
4128         
4129         for ( i=0; i<num_domains; i++ ) {
4130                 if ( strequal(name, dom_list[i].domain_name) ||
4131                      strequal(name, dom_list[i].dns_name) )
4132                 {
4133                         DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
4134                                   name));
4135                         
4136                         d = TALLOC_P( ctx, struct winbindd_tdc_domain );
4137                         if ( !d )
4138                                 break;                  
4139                         
4140                         d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
4141                         d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
4142                         sid_copy( &d->sid, &dom_list[i].sid );
4143                         d->trust_flags   = dom_list[i].trust_flags;
4144                         d->trust_type    = dom_list[i].trust_type;
4145                         d->trust_attribs = dom_list[i].trust_attribs;
4146
4147                         break;
4148                 }
4149         }
4150
4151         TALLOC_FREE( dom_list );
4152         
4153         return d;       
4154 }
4155
4156
4157 /*********************************************************************
4158  ********************************************************************/
4159
4160 void wcache_tdc_clear( void )
4161 {
4162         if ( !init_wcache() )
4163                 return;
4164
4165         wcache_tdc_store_list( NULL, 0 );
4166         
4167         return; 
4168 }
4169
4170
4171 /*********************************************************************
4172  ********************************************************************/
4173
4174 static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
4175                                     NTSTATUS status,
4176                                     const DOM_SID *user_sid,
4177                                     const char *homedir,
4178                                     const char *shell,
4179                                     const char *gecos,
4180                                     uint32 gid)
4181 {
4182         struct cache_entry *centry;
4183         fstring tmp;
4184
4185         if ( (centry = centry_start(domain, status)) == NULL )
4186                 return;
4187
4188         centry_put_string( centry, homedir );
4189         centry_put_string( centry, shell );
4190         centry_put_string( centry, gecos );
4191         centry_put_uint32( centry, gid );
4192         
4193         centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
4194
4195         DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
4196
4197         centry_free(centry);
4198 }
4199
4200 NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
4201                               const DOM_SID *user_sid,
4202                               TALLOC_CTX *ctx,
4203                               ADS_STRUCT *ads, LDAPMessage *msg,
4204                               char **homedir, char **shell, char **gecos,
4205                               gid_t *p_gid)
4206 {
4207         struct winbind_cache *cache = get_cache(domain);
4208         struct cache_entry *centry = NULL;
4209         NTSTATUS nt_status;
4210         fstring tmp;
4211
4212         if (!cache->tdb)
4213                 goto do_query;
4214
4215         centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
4216                               sid_to_fstring(tmp, user_sid));
4217         
4218         if (!centry)
4219                 goto do_query;
4220
4221         *homedir = centry_string( centry, ctx );
4222         *shell   = centry_string( centry, ctx );
4223         *gecos   = centry_string( centry, ctx );
4224         *p_gid   = centry_uint32( centry );     
4225
4226         centry_free(centry);
4227
4228         DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
4229                   sid_string_dbg(user_sid)));
4230
4231         return NT_STATUS_OK;
4232
4233 do_query:
4234         
4235         nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
4236                                   homedir, shell, gecos, p_gid );
4237
4238         DEBUG(10, ("nss_get_info returned %s\n", nt_errstr(nt_status)));
4239
4240         if ( NT_STATUS_IS_OK(nt_status) ) {
4241                 DEBUG(10, ("result:\n\thomedir = '%s'\n", *homedir));
4242                 DEBUGADD(10, ("\tshell = '%s'\n", *shell));
4243                 DEBUGADD(10, ("\tgecos = '%s'\n", *gecos));
4244                 DEBUGADD(10, ("\tgid = '%u'\n", (unsigned int)*p_gid));
4245
4246                 wcache_save_user_pwinfo( domain, nt_status, user_sid,
4247                                          *homedir, *shell, *gecos, *p_gid );
4248         }       
4249
4250         if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
4251                 DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
4252                          domain->name ));
4253                 set_domain_offline( domain );
4254         }
4255
4256         return nt_status;       
4257 }
4258
4259
4260 /* the cache backend methods are exposed via this structure */
4261 struct winbindd_methods cache_methods = {
4262         true,
4263         query_user_list,
4264         enum_dom_groups,
4265         enum_local_groups,
4266         name_to_sid,
4267         sid_to_name,
4268         rids_to_names,
4269         query_user,
4270         lookup_usergroups,
4271         lookup_useraliases,
4272         lookup_groupmem,
4273         sequence_number,
4274         lockout_policy,
4275         password_policy,
4276         trusted_domains
4277 };