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