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