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