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