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