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