r7994: This adds support in Winbindd's "security = ads"-mode to retrieve the POSIX
[abartlet/samba.git/.git] / source3 / nsswitch / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003
8    Copyright (C) Volker Lendecke 2005
9    
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 struct winbind_cache {
33         TDB_CONTEXT *tdb;
34 };
35
36 struct cache_entry {
37         NTSTATUS status;
38         uint32 sequence_number;
39         uint8 *data;
40         uint32 len, ofs;
41 };
42
43 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
44
45 static struct winbind_cache *wcache;
46
47 /* flush the cache */
48 void wcache_flush_cache(void)
49 {
50         extern BOOL opt_nocache;
51
52         if (!wcache)
53                 return;
54         if (wcache->tdb) {
55                 tdb_close(wcache->tdb);
56                 wcache->tdb = NULL;
57         }
58         if (opt_nocache)
59                 return;
60
61         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
62                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
63
64         if (!wcache->tdb) {
65                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
66         }
67         DEBUG(10,("wcache_flush_cache success\n"));
68 }
69
70 void winbindd_check_cache_size(time_t t)
71 {
72         static time_t last_check_time;
73         struct stat st;
74
75         if (last_check_time == (time_t)0)
76                 last_check_time = t;
77
78         if (t - last_check_time < 60 && t - last_check_time > 0)
79                 return;
80
81         if (wcache == NULL || wcache->tdb == NULL) {
82                 DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
83                 return;
84         }
85
86         if (fstat(wcache->tdb->fd, &st) == -1) {
87                 DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
88                 return;
89         }
90
91         if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
92                 DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
93                         (unsigned long)st.st_size,
94                         (unsigned long)WINBINDD_MAX_CACHE_SIZE));
95                 wcache_flush_cache();
96         }
97 }
98
99 /* get the winbind_cache structure */
100 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
101 {
102         struct winbind_cache *ret = wcache;
103
104         if (!domain->backend) {
105                 extern struct winbindd_methods reconnect_methods;
106                 switch (lp_security()) {
107 #ifdef HAVE_ADS
108                 case SEC_ADS: {
109                         extern struct winbindd_methods ads_methods;
110                         /* always obey the lp_security parameter for our domain */
111                         if (domain->primary) {
112                                 domain->backend = &ads_methods;
113                                 break;
114                         }
115
116                         /* only use ADS for native modes at the momment.
117                            The problem is the correct detection of mixed 
118                            mode domains from NT4 BDC's    --jerry */
119                         
120                         if ( domain->native_mode ) {
121                                 DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
122                                         domain->name));
123                                 domain->backend = &ads_methods;
124                                 break;
125                         }
126
127                         /* fall through */
128                 }       
129 #endif
130                 default:
131                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
132                                 domain->name));
133                         domain->backend = &reconnect_methods;
134                 }
135         }
136
137         if (ret)
138                 return ret;
139         
140         ret = SMB_XMALLOC_P(struct winbind_cache);
141         ZERO_STRUCTP(ret);
142
143         wcache = ret;
144         wcache_flush_cache();
145
146         return ret;
147 }
148
149 /*
150   free a centry structure
151 */
152 static void centry_free(struct cache_entry *centry)
153 {
154         if (!centry)
155                 return;
156         SAFE_FREE(centry->data);
157         free(centry);
158 }
159
160 /*
161   pull a uint32 from a cache entry 
162 */
163 static uint32 centry_uint32(struct cache_entry *centry)
164 {
165         uint32 ret;
166         if (centry->len - centry->ofs < 4) {
167                 DEBUG(0,("centry corruption? needed 4 bytes, have %d\n", 
168                          centry->len - centry->ofs));
169                 smb_panic("centry_uint32");
170         }
171         ret = IVAL(centry->data, centry->ofs);
172         centry->ofs += 4;
173         return ret;
174 }
175
176 /*
177   pull a uint8 from a cache entry 
178 */
179 static uint8 centry_uint8(struct cache_entry *centry)
180 {
181         uint8 ret;
182         if (centry->len - centry->ofs < 1) {
183                 DEBUG(0,("centry corruption? needed 1 bytes, have %d\n", 
184                          centry->len - centry->ofs));
185                 smb_panic("centry_uint32");
186         }
187         ret = CVAL(centry->data, centry->ofs);
188         centry->ofs += 1;
189         return ret;
190 }
191
192 /* pull a string from a cache entry, using the supplied
193    talloc context 
194 */
195 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
196 {
197         uint32 len;
198         char *ret;
199
200         len = centry_uint8(centry);
201
202         if (len == 0xFF) {
203                 /* a deliberate NULL string */
204                 return NULL;
205         }
206
207         if (centry->len - centry->ofs < len) {
208                 DEBUG(0,("centry corruption? needed %d bytes, have %d\n", 
209                          len, centry->len - centry->ofs));
210                 smb_panic("centry_string");
211         }
212
213         if (mem_ctx != NULL)
214                 ret = TALLOC(mem_ctx, len+1);
215         else
216                 ret = SMB_MALLOC(len+1);
217         if (!ret) {
218                 smb_panic("centry_string out of memory\n");
219         }
220         memcpy(ret,centry->data + centry->ofs, len);
221         ret[len] = 0;
222         centry->ofs += len;
223         return ret;
224 }
225
226 /* pull a string from a cache entry, using the supplied
227    talloc context 
228 */
229 static BOOL centry_sid(struct cache_entry *centry, DOM_SID *sid)
230 {
231         char *sid_string;
232         sid_string = centry_string(centry, NULL);
233         if (!string_to_sid(sid, sid_string)) {
234                 return False;
235         }
236         SAFE_FREE(sid_string);
237         return True;
238 }
239
240 /* the server is considered down if it can't give us a sequence number */
241 static BOOL wcache_server_down(struct winbindd_domain *domain)
242 {
243         BOOL ret;
244
245         if (!wcache->tdb)
246                 return False;
247
248         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
249
250         if (ret)
251                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
252                         domain->name ));
253         return ret;
254 }
255
256 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
257 {
258         TDB_DATA data;
259         fstring key;
260         uint32 time_diff;
261         
262         if (!wcache->tdb) {
263                 DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
264                 return NT_STATUS_UNSUCCESSFUL;
265         }
266                 
267         fstr_sprintf( key, "SEQNUM/%s", domain->name );
268         
269         data = tdb_fetch_bystring( wcache->tdb, key );
270         if ( !data.dptr || data.dsize!=8 ) {
271                 DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
272                 return NT_STATUS_UNSUCCESSFUL;
273         }
274         
275         domain->sequence_number = IVAL(data.dptr, 0);
276         domain->last_seq_check  = IVAL(data.dptr, 4);
277         
278         SAFE_FREE(data.dptr);
279
280         /* have we expired? */
281         
282         time_diff = now - domain->last_seq_check;
283         if ( time_diff > lp_winbind_cache_time() ) {
284                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
285                         domain->name, domain->sequence_number,
286                         (uint32)domain->last_seq_check));
287                 return NT_STATUS_UNSUCCESSFUL;
288         }
289
290         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
291                 domain->name, domain->sequence_number, 
292                 (uint32)domain->last_seq_check));
293
294         return NT_STATUS_OK;
295 }
296
297 static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
298 {
299         TDB_DATA data, key;
300         fstring key_str;
301         char buf[8];
302         
303         if (!wcache->tdb) {
304                 DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
305                 return NT_STATUS_UNSUCCESSFUL;
306         }
307                 
308         fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
309         key.dptr = key_str;
310         key.dsize = strlen(key_str)+1;
311         
312         SIVAL(buf, 0, domain->sequence_number);
313         SIVAL(buf, 4, domain->last_seq_check);
314         data.dptr = buf;
315         data.dsize = 8;
316         
317         if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
318                 DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
319                 return NT_STATUS_UNSUCCESSFUL;
320         }
321
322         DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n", 
323                 domain->name, domain->sequence_number, 
324                 (uint32)domain->last_seq_check));
325         
326         return NT_STATUS_OK;
327 }
328
329 /*
330   refresh the domain sequence number. If force is True
331   then always refresh it, no matter how recently we fetched it
332 */
333
334 static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
335 {
336         NTSTATUS status;
337         unsigned time_diff;
338         time_t t = time(NULL);
339         unsigned cache_time = lp_winbind_cache_time();
340
341         get_cache( domain );
342
343 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
344         /* trying to reconnect is expensive, don't do it too often */
345         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
346                 cache_time *= 8;
347         }
348 #endif
349
350         time_diff = t - domain->last_seq_check;
351
352         /* see if we have to refetch the domain sequence number */
353         if (!force && (time_diff < cache_time)) {
354                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
355                 goto done;
356         }
357         
358         /* try to get the sequence number from the tdb cache first */
359         /* this will update the timestamp as well */
360         
361         status = fetch_cache_seqnum( domain, t );
362         if ( NT_STATUS_IS_OK(status) )
363                 goto done;      
364
365         /* important! make sure that we know if this is a native 
366            mode domain or not */
367
368         if ( !domain->initialized )
369                 set_dc_type_and_flags( domain );
370
371         status = domain->backend->sequence_number(domain, &domain->sequence_number);
372
373         if (!NT_STATUS_IS_OK(status)) {
374                 domain->sequence_number = DOM_SEQUENCE_NONE;
375         }
376         
377         domain->last_status = status;
378         domain->last_seq_check = time(NULL);
379         
380         /* save the new sequence number ni the cache */
381         store_cache_seqnum( domain );
382
383 done:
384         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
385                    domain->name, domain->sequence_number));
386
387         return;
388 }
389
390 /*
391   decide if a cache entry has expired
392 */
393 static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
394 {
395         /* if the server is OK and our cache entry came from when it was down then
396            the entry is invalid */
397         if (domain->sequence_number != DOM_SEQUENCE_NONE && 
398             centry->sequence_number == DOM_SEQUENCE_NONE) {
399                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
400                         keystr, domain->name ));
401                 return True;
402         }
403
404         /* if the server is down or the cache entry is not older than the
405            current sequence number then it is OK */
406         if (wcache_server_down(domain) || 
407             centry->sequence_number == domain->sequence_number) {
408                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
409                         keystr, domain->name ));
410                 return False;
411         }
412
413         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
414                 keystr, domain->name ));
415
416         /* it's expired */
417         return True;
418 }
419
420 /*
421   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
422   number and return status
423 */
424 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
425                                         struct winbindd_domain *domain,
426                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
427 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
428                                         struct winbindd_domain *domain,
429                                         const char *format, ...)
430 {
431         va_list ap;
432         char *kstr;
433         TDB_DATA data;
434         struct cache_entry *centry;
435         TDB_DATA key;
436
437         refresh_sequence_number(domain, False);
438
439         va_start(ap, format);
440         smb_xvasprintf(&kstr, format, ap);
441         va_end(ap);
442         
443         key.dptr = kstr;
444         key.dsize = strlen(kstr);
445         data = tdb_fetch(wcache->tdb, key);
446         if (!data.dptr) {
447                 /* a cache miss */
448                 free(kstr);
449                 return NULL;
450         }
451
452         centry = SMB_XMALLOC_P(struct cache_entry);
453         centry->data = (unsigned char *)data.dptr;
454         centry->len = data.dsize;
455         centry->ofs = 0;
456
457         if (centry->len < 8) {
458                 /* huh? corrupt cache? */
459                 DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
460                         kstr, domain->name ));
461                 centry_free(centry);
462                 free(kstr);
463                 return NULL;
464         }
465         
466         centry->status = NT_STATUS(centry_uint32(centry));
467         centry->sequence_number = centry_uint32(centry);
468
469         if (centry_expired(domain, kstr, centry)) {
470                 extern BOOL opt_dual_daemon;
471
472                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
473                          kstr, domain->name ));
474
475                 if (opt_dual_daemon) {
476                         extern BOOL background_process;
477                         background_process = True;
478                         DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
479                                  kstr, domain->name ));
480                 } else {
481                         centry_free(centry);
482                         free(kstr);
483                         return NULL;
484                 }
485         }
486
487         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
488                  kstr, domain->name ));
489
490         free(kstr);
491         return centry;
492 }
493
494 /*
495   make sure we have at least len bytes available in a centry 
496 */
497 static void centry_expand(struct cache_entry *centry, uint32 len)
498 {
499         uint8 *p;
500         if (centry->len - centry->ofs >= len)
501                 return;
502         centry->len *= 2;
503         p = SMB_REALLOC(centry->data, centry->len);
504         if (!p) {
505                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
506                 smb_panic("out of memory in centry_expand");
507         }
508         centry->data = p;
509 }
510
511 /*
512   push a uint32 into a centry 
513 */
514 static void centry_put_uint32(struct cache_entry *centry, uint32 v)
515 {
516         centry_expand(centry, 4);
517         SIVAL(centry->data, centry->ofs, v);
518         centry->ofs += 4;
519 }
520
521 /*
522   push a uint8 into a centry 
523 */
524 static void centry_put_uint8(struct cache_entry *centry, uint8 v)
525 {
526         centry_expand(centry, 1);
527         SCVAL(centry->data, centry->ofs, v);
528         centry->ofs += 1;
529 }
530
531 /* 
532    push a string into a centry 
533  */
534 static void centry_put_string(struct cache_entry *centry, const char *s)
535 {
536         int len;
537
538         if (!s) {
539                 /* null strings are marked as len 0xFFFF */
540                 centry_put_uint8(centry, 0xFF);
541                 return;
542         }
543
544         len = strlen(s);
545         /* can't handle more than 254 char strings. Truncating is probably best */
546         if (len > 254)
547                 len = 254;
548         centry_put_uint8(centry, len);
549         centry_expand(centry, len);
550         memcpy(centry->data + centry->ofs, s, len);
551         centry->ofs += len;
552 }
553
554 static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
555 {
556         fstring sid_string;
557         centry_put_string(centry, sid_to_string(sid_string, sid));
558 }
559
560 /*
561   start a centry for output. When finished, call centry_end()
562 */
563 struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
564 {
565         struct cache_entry *centry;
566
567         if (!wcache->tdb)
568                 return NULL;
569
570         centry = SMB_XMALLOC_P(struct cache_entry);
571
572         centry->len = 8192; /* reasonable default */
573         centry->data = SMB_XMALLOC_ARRAY(char, centry->len);
574         centry->ofs = 0;
575         centry->sequence_number = domain->sequence_number;
576         centry_put_uint32(centry, NT_STATUS_V(status));
577         centry_put_uint32(centry, centry->sequence_number);
578         return centry;
579 }
580
581 /*
582   finish a centry and write it to the tdb
583 */
584 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
585 static void centry_end(struct cache_entry *centry, const char *format, ...)
586 {
587         va_list ap;
588         char *kstr;
589         TDB_DATA key, data;
590
591         va_start(ap, format);
592         smb_xvasprintf(&kstr, format, ap);
593         va_end(ap);
594
595         key.dptr = kstr;
596         key.dsize = strlen(kstr);
597         data.dptr = (char *)centry->data;
598         data.dsize = centry->ofs;
599
600         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
601         free(kstr);
602 }
603
604 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
605                                     NTSTATUS status, const char *domain_name,
606                                     const char *name, const DOM_SID *sid, 
607                                     enum SID_NAME_USE type)
608 {
609         struct cache_entry *centry;
610         fstring uname;
611
612         centry = centry_start(domain, status);
613         if (!centry)
614                 return;
615         centry_put_uint32(centry, type);
616         centry_put_sid(centry, sid);
617         fstrcpy(uname, name);
618         strupper_m(uname);
619         centry_end(centry, "NS/%s/%s", domain_name, uname);
620         DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
621                   sid_string_static(sid)));
622         centry_free(centry);
623 }
624
625 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
626                                     const DOM_SID *sid, const char *domain_name, const char *name, enum SID_NAME_USE type)
627 {
628         struct cache_entry *centry;
629         fstring sid_string;
630
631         centry = centry_start(domain, status);
632         if (!centry)
633                 return;
634         if (NT_STATUS_IS_OK(status)) {
635                 centry_put_uint32(centry, type);
636                 centry_put_string(centry, domain_name);
637                 centry_put_string(centry, name);
638         }
639         centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
640         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
641         centry_free(centry);
642 }
643
644
645 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
646 {
647         struct cache_entry *centry;
648         fstring sid_string;
649
650         centry = centry_start(domain, status);
651         if (!centry)
652                 return;
653         centry_put_string(centry, info->acct_name);
654         centry_put_string(centry, info->full_name);
655         centry_put_string(centry, info->homedir);
656         centry_put_string(centry, info->shell);
657         centry_put_sid(centry, &info->user_sid);
658         centry_put_sid(centry, &info->group_sid);
659         centry_end(centry, "U/%s", sid_to_string(sid_string, &info->user_sid));
660         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
661         centry_free(centry);
662 }
663
664
665 /* Query display info. This is the basic user list fn */
666 static NTSTATUS query_user_list(struct winbindd_domain *domain,
667                                 TALLOC_CTX *mem_ctx,
668                                 uint32 *num_entries, 
669                                 WINBIND_USERINFO **info)
670 {
671         struct winbind_cache *cache = get_cache(domain);
672         struct cache_entry *centry = NULL;
673         NTSTATUS status;
674         unsigned int i, retry;
675
676         if (!cache->tdb)
677                 goto do_query;
678
679         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
680         if (!centry)
681                 goto do_query;
682
683         *num_entries = centry_uint32(centry);
684         
685         if (*num_entries == 0)
686                 goto do_cached;
687
688         (*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
689         if (! (*info))
690                 smb_panic("query_user_list out of memory");
691         for (i=0; i<(*num_entries); i++) {
692                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
693                 (*info)[i].full_name = centry_string(centry, mem_ctx);
694                 (*info)[i].homedir = centry_string(centry, mem_ctx);
695                 (*info)[i].shell = centry_string(centry, mem_ctx);
696                 centry_sid(centry, &(*info)[i].user_sid);
697                 centry_sid(centry, &(*info)[i].group_sid);
698         }
699
700 do_cached:      
701         status = centry->status;
702
703         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
704                 domain->name, get_friendly_nt_error_msg(status) ));
705
706         centry_free(centry);
707         return status;
708
709 do_query:
710         *num_entries = 0;
711         *info = NULL;
712
713         /* Return status value returned by seq number check */
714
715         if (!NT_STATUS_IS_OK(domain->last_status))
716                 return domain->last_status;
717
718         /* Put the query_user_list() in a retry loop.  There appears to be
719          * some bug either with Windows 2000 or Samba's handling of large
720          * rpc replies.  This manifests itself as sudden disconnection
721          * at a random point in the enumeration of a large (60k) user list.
722          * The retry loop simply tries the operation again. )-:  It's not
723          * pretty but an acceptable workaround until we work out what the
724          * real problem is. */
725
726         retry = 0;
727         do {
728
729                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
730                         domain->name ));
731
732                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
733                 if (!NT_STATUS_IS_OK(status))
734                         DEBUG(3, ("query_user_list: returned 0x%08x, "
735                                   "retrying\n", NT_STATUS_V(status)));
736                         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
737                                 DEBUG(3, ("query_user_list: flushing "
738                                           "connection cache\n"));
739                                 invalidate_cm_connection(&domain->conn);
740                         }
741
742         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
743                  (retry++ < 5));
744
745         /* and save it */
746         refresh_sequence_number(domain, False);
747         centry = centry_start(domain, status);
748         if (!centry)
749                 goto skip_save;
750         centry_put_uint32(centry, *num_entries);
751         for (i=0; i<(*num_entries); i++) {
752                 centry_put_string(centry, (*info)[i].acct_name);
753                 centry_put_string(centry, (*info)[i].full_name);
754                 centry_put_string(centry, (*info)[i].homedir);
755                 centry_put_string(centry, (*info)[i].shell);
756                 centry_put_sid(centry, &(*info)[i].user_sid);
757                 centry_put_sid(centry, &(*info)[i].group_sid);
758                 if (domain->backend->consistent) {
759                         /* when the backend is consistent we can pre-prime some mappings */
760                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
761                                                 domain->name,
762                                                 (*info)[i].acct_name, 
763                                                 &(*info)[i].user_sid,
764                                                 SID_NAME_USER);
765                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
766                                                 &(*info)[i].user_sid,
767                                                 domain->name,
768                                                 (*info)[i].acct_name, 
769                                                 SID_NAME_USER);
770                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
771                 }
772         }       
773         centry_end(centry, "UL/%s", domain->name);
774         centry_free(centry);
775
776 skip_save:
777         return status;
778 }
779
780 /* list all domain groups */
781 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
782                                 TALLOC_CTX *mem_ctx,
783                                 uint32 *num_entries, 
784                                 struct acct_info **info)
785 {
786         struct winbind_cache *cache = get_cache(domain);
787         struct cache_entry *centry = NULL;
788         NTSTATUS status;
789         unsigned int i;
790
791         if (!cache->tdb)
792                 goto do_query;
793
794         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
795         if (!centry)
796                 goto do_query;
797
798         *num_entries = centry_uint32(centry);
799         
800         if (*num_entries == 0)
801                 goto do_cached;
802
803         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
804         if (! (*info))
805                 smb_panic("enum_dom_groups out of memory");
806         for (i=0; i<(*num_entries); i++) {
807                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
808                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
809                 (*info)[i].rid = centry_uint32(centry);
810         }
811
812 do_cached:      
813         status = centry->status;
814
815         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
816                 domain->name, get_friendly_nt_error_msg(status) ));
817
818         centry_free(centry);
819         return status;
820
821 do_query:
822         *num_entries = 0;
823         *info = NULL;
824
825         /* Return status value returned by seq number check */
826
827         if (!NT_STATUS_IS_OK(domain->last_status))
828                 return domain->last_status;
829
830         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
831                 domain->name ));
832
833         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
834
835         /* and save it */
836         refresh_sequence_number(domain, False);
837         centry = centry_start(domain, status);
838         if (!centry)
839                 goto skip_save;
840         centry_put_uint32(centry, *num_entries);
841         for (i=0; i<(*num_entries); i++) {
842                 centry_put_string(centry, (*info)[i].acct_name);
843                 centry_put_string(centry, (*info)[i].acct_desc);
844                 centry_put_uint32(centry, (*info)[i].rid);
845         }       
846         centry_end(centry, "GL/%s/domain", domain->name);
847         centry_free(centry);
848
849 skip_save:
850         return status;
851 }
852
853 /* list all domain groups */
854 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
855                                 TALLOC_CTX *mem_ctx,
856                                 uint32 *num_entries, 
857                                 struct acct_info **info)
858 {
859         struct winbind_cache *cache = get_cache(domain);
860         struct cache_entry *centry = NULL;
861         NTSTATUS status;
862         unsigned int i;
863
864         if (!cache->tdb)
865                 goto do_query;
866
867         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
868         if (!centry)
869                 goto do_query;
870
871         *num_entries = centry_uint32(centry);
872         
873         if (*num_entries == 0)
874                 goto do_cached;
875
876         (*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
877         if (! (*info))
878                 smb_panic("enum_dom_groups out of memory");
879         for (i=0; i<(*num_entries); i++) {
880                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
881                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
882                 (*info)[i].rid = centry_uint32(centry);
883         }
884
885 do_cached:      
886
887         /* If we are returning cached data and the domain controller
888            is down then we don't know whether the data is up to date
889            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
890            indicate this. */
891
892         if (wcache_server_down(domain)) {
893                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
894                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
895         } else
896                 status = centry->status;
897
898         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
899                 domain->name, get_friendly_nt_error_msg(status) ));
900
901         centry_free(centry);
902         return status;
903
904 do_query:
905         *num_entries = 0;
906         *info = NULL;
907
908         /* Return status value returned by seq number check */
909
910         if (!NT_STATUS_IS_OK(domain->last_status))
911                 return domain->last_status;
912
913         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
914                 domain->name ));
915
916         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
917
918         /* and save it */
919         refresh_sequence_number(domain, False);
920         centry = centry_start(domain, status);
921         if (!centry)
922                 goto skip_save;
923         centry_put_uint32(centry, *num_entries);
924         for (i=0; i<(*num_entries); i++) {
925                 centry_put_string(centry, (*info)[i].acct_name);
926                 centry_put_string(centry, (*info)[i].acct_desc);
927                 centry_put_uint32(centry, (*info)[i].rid);
928         }
929         centry_end(centry, "GL/%s/local", domain->name);
930         centry_free(centry);
931
932 skip_save:
933         return status;
934 }
935
936 /* convert a single name to a sid in a domain */
937 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
938                             TALLOC_CTX *mem_ctx,
939                             const char *domain_name,
940                             const char *name,
941                             DOM_SID *sid,
942                             enum SID_NAME_USE *type)
943 {
944         struct winbind_cache *cache = get_cache(domain);
945         struct cache_entry *centry = NULL;
946         NTSTATUS status;
947         fstring uname;
948
949         if (!cache->tdb)
950                 goto do_query;
951
952         fstrcpy(uname, name);
953         strupper_m(uname);
954         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
955         if (!centry)
956                 goto do_query;
957         *type = (enum SID_NAME_USE)centry_uint32(centry);
958         centry_sid(centry, sid);
959         status = centry->status;
960
961         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
962                 domain->name, get_friendly_nt_error_msg(status) ));
963
964         centry_free(centry);
965         return status;
966
967 do_query:
968         ZERO_STRUCTP(sid);
969
970         /* If the seq number check indicated that there is a problem
971          * with this DC, then return that status... except for
972          * access_denied.  This is special because the dc may be in
973          * "restrict anonymous = 1" mode, in which case it will deny
974          * most unauthenticated operations, but *will* allow the LSA
975          * name-to-sid that we try as a fallback. */
976
977         if (!(NT_STATUS_IS_OK(domain->last_status)
978               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
979                 return domain->last_status;
980
981         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
982                 domain->name ));
983
984         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
985
986         /* and save it */
987         wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
988
989         /* We can't save the sid to name mapping as we don't know the
990            correct case of the name without looking it up */
991
992         return status;
993 }
994
995 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
996    given */
997 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
998                             TALLOC_CTX *mem_ctx,
999                             const DOM_SID *sid,
1000                             char **domain_name,
1001                             char **name,
1002                             enum SID_NAME_USE *type)
1003 {
1004         struct winbind_cache *cache = get_cache(domain);
1005         struct cache_entry *centry = NULL;
1006         NTSTATUS status;
1007         fstring sid_string;
1008
1009         if (!cache->tdb)
1010                 goto do_query;
1011
1012         centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid));
1013         if (!centry)
1014                 goto do_query;
1015         if (NT_STATUS_IS_OK(centry->status)) {
1016                 *type = (enum SID_NAME_USE)centry_uint32(centry);
1017                 *domain_name = centry_string(centry, mem_ctx);
1018                 *name = centry_string(centry, mem_ctx);
1019         }
1020         status = centry->status;
1021
1022         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
1023                 domain->name, get_friendly_nt_error_msg(status) ));
1024
1025         centry_free(centry);
1026         return status;
1027
1028 do_query:
1029         *name = NULL;
1030         *domain_name = NULL;
1031
1032         /* If the seq number check indicated that there is a problem
1033          * with this DC, then return that status... except for
1034          * access_denied.  This is special because the dc may be in
1035          * "restrict anonymous = 1" mode, in which case it will deny
1036          * most unauthenticated operations, but *will* allow the LSA
1037          * sid-to-name that we try as a fallback. */
1038
1039         if (!(NT_STATUS_IS_OK(domain->last_status)
1040               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1041                 return domain->last_status;
1042
1043         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1044                 domain->name ));
1045
1046         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1047
1048         /* and save it */
1049         refresh_sequence_number(domain, False);
1050         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
1051
1052         /* We can't save the name to sid mapping here, as with sid history a
1053          * later name2sid would give the wrong sid. */
1054
1055         return status;
1056 }
1057
1058
1059 /* Lookup user information from a rid */
1060 static NTSTATUS query_user(struct winbindd_domain *domain, 
1061                            TALLOC_CTX *mem_ctx, 
1062                            const DOM_SID *user_sid, 
1063                            WINBIND_USERINFO *info)
1064 {
1065         struct winbind_cache *cache = get_cache(domain);
1066         struct cache_entry *centry = NULL;
1067         NTSTATUS status;
1068
1069         if (!cache->tdb)
1070                 goto do_query;
1071
1072         centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
1073         
1074         /* If we have an access denied cache entry and a cached info3 in the
1075            samlogon cache then do a query.  This will force the rpc back end
1076            to return the info3 data. */
1077
1078         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1079             netsamlogon_cache_have(user_sid)) {
1080                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1081                 domain->last_status = NT_STATUS_OK;
1082                 centry_free(centry);
1083                 goto do_query;
1084         }
1085         
1086         if (!centry)
1087                 goto do_query;
1088
1089         info->acct_name = centry_string(centry, mem_ctx);
1090         info->full_name = centry_string(centry, mem_ctx);
1091         info->homedir = centry_string(centry, mem_ctx);
1092         info->shell = centry_string(centry, mem_ctx);
1093         centry_sid(centry, &info->user_sid);
1094         centry_sid(centry, &info->group_sid);
1095         status = centry->status;
1096
1097         DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
1098                 domain->name, get_friendly_nt_error_msg(status) ));
1099
1100         centry_free(centry);
1101         return status;
1102
1103 do_query:
1104         ZERO_STRUCTP(info);
1105
1106         /* Return status value returned by seq number check */
1107
1108         if (!NT_STATUS_IS_OK(domain->last_status))
1109                 return domain->last_status;
1110         
1111         DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
1112                 domain->name ));
1113
1114         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
1115
1116         /* and save it */
1117         refresh_sequence_number(domain, False);
1118         wcache_save_user(domain, status, info);
1119
1120         return status;
1121 }
1122
1123
1124 /* Lookup groups a user is a member of. */
1125 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
1126                                   TALLOC_CTX *mem_ctx,
1127                                   const DOM_SID *user_sid, 
1128                                   uint32 *num_groups, DOM_SID **user_gids)
1129 {
1130         struct winbind_cache *cache = get_cache(domain);
1131         struct cache_entry *centry = NULL;
1132         NTSTATUS status;
1133         unsigned int i;
1134         fstring sid_string;
1135
1136         if (!cache->tdb)
1137                 goto do_query;
1138
1139         centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
1140         
1141         /* If we have an access denied cache entry and a cached info3 in the
1142            samlogon cache then do a query.  This will force the rpc back end
1143            to return the info3 data. */
1144
1145         if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
1146             netsamlogon_cache_have(user_sid)) {
1147                 DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
1148                 domain->last_status = NT_STATUS_OK;
1149                 centry_free(centry);
1150                 goto do_query;
1151         }
1152         
1153         if (!centry)
1154                 goto do_query;
1155
1156         *num_groups = centry_uint32(centry);
1157         
1158         if (*num_groups == 0)
1159                 goto do_cached;
1160
1161         (*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
1162         if (! (*user_gids))
1163                 smb_panic("lookup_usergroups out of memory");
1164         for (i=0; i<(*num_groups); i++) {
1165                 centry_sid(centry, &(*user_gids)[i]);
1166         }
1167
1168 do_cached:      
1169         status = centry->status;
1170
1171         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
1172                 domain->name, get_friendly_nt_error_msg(status) ));
1173
1174         centry_free(centry);
1175         return status;
1176
1177 do_query:
1178         (*num_groups) = 0;
1179         (*user_gids) = NULL;
1180
1181         /* Return status value returned by seq number check */
1182
1183         if (!NT_STATUS_IS_OK(domain->last_status))
1184                 return domain->last_status;
1185
1186         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
1187                 domain->name ));
1188
1189         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
1190
1191         /* and save it */
1192         refresh_sequence_number(domain, False);
1193         centry = centry_start(domain, status);
1194         if (!centry)
1195                 goto skip_save;
1196         centry_put_uint32(centry, *num_groups);
1197         for (i=0; i<(*num_groups); i++) {
1198                 centry_put_sid(centry, &(*user_gids)[i]);
1199         }       
1200         centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
1201         centry_free(centry);
1202
1203 skip_save:
1204         return status;
1205 }
1206
1207 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1208                                    TALLOC_CTX *mem_ctx,
1209                                    uint32 num_sids, const DOM_SID *sids,
1210                                    uint32 *num_aliases, uint32 **alias_rids)
1211 {
1212         struct winbind_cache *cache = get_cache(domain);
1213         struct cache_entry *centry = NULL;
1214         NTSTATUS status;
1215         char *sidlist = talloc_strdup(mem_ctx, "");
1216         int i;
1217
1218         if (!cache->tdb)
1219                 goto do_query;
1220
1221         if (num_sids == 0) {
1222                 *num_aliases = 0;
1223                 *alias_rids = NULL;
1224                 return NT_STATUS_OK;
1225         }
1226
1227         /* We need to cache indexed by the whole list of SIDs, the aliases
1228          * resulting might come from any of the SIDs. */
1229
1230         for (i=0; i<num_sids; i++) {
1231                 sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
1232                                           sid_string_static(&sids[i]));
1233                 if (sidlist == NULL)
1234                         return NT_STATUS_NO_MEMORY;
1235         }
1236
1237         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
1238
1239         if (!centry)
1240                 goto do_query;
1241
1242         *num_aliases = centry_uint32(centry);
1243         *alias_rids = NULL;
1244
1245         (*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
1246
1247         if ((*num_aliases != 0) && ((*alias_rids) == NULL))
1248                 return NT_STATUS_NO_MEMORY;
1249
1250         for (i=0; i<(*num_aliases); i++)
1251                 (*alias_rids)[i] = centry_uint32(centry);
1252
1253         status = centry->status;
1254
1255         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain %s "
1256                   "status %s\n", domain->name,
1257                   get_friendly_nt_error_msg(status)));
1258
1259         centry_free(centry);
1260         return status;
1261
1262  do_query:
1263         (*num_aliases) = 0;
1264         (*alias_rids) = NULL;
1265
1266         if (!NT_STATUS_IS_OK(domain->last_status))
1267                 return domain->last_status;
1268
1269         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
1270                   "for domain %s\n", domain->name ));
1271
1272         status = domain->backend->lookup_useraliases(domain, mem_ctx,
1273                                                      num_sids, sids,
1274                                                      num_aliases, alias_rids);
1275
1276         /* and save it */
1277         refresh_sequence_number(domain, False);
1278         centry = centry_start(domain, status);
1279         if (!centry)
1280                 goto skip_save;
1281         centry_put_uint32(centry, *num_aliases);
1282         for (i=0; i<(*num_aliases); i++)
1283                 centry_put_uint32(centry, (*alias_rids)[i]);
1284         centry_end(centry, "UA%s", sidlist);
1285         centry_free(centry);
1286
1287  skip_save:
1288         return status;
1289 }
1290
1291
1292 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1293                                 TALLOC_CTX *mem_ctx,
1294                                 const DOM_SID *group_sid, uint32 *num_names, 
1295                                 DOM_SID **sid_mem, char ***names, 
1296                                 uint32 **name_types)
1297 {
1298         struct winbind_cache *cache = get_cache(domain);
1299         struct cache_entry *centry = NULL;
1300         NTSTATUS status;
1301         unsigned int i;
1302         fstring sid_string;
1303
1304         if (!cache->tdb)
1305                 goto do_query;
1306
1307         centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
1308         if (!centry)
1309                 goto do_query;
1310
1311         *num_names = centry_uint32(centry);
1312         
1313         if (*num_names == 0)
1314                 goto do_cached;
1315
1316         (*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
1317         (*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
1318         (*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
1319
1320         if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
1321                 smb_panic("lookup_groupmem out of memory");
1322         }
1323
1324         for (i=0; i<(*num_names); i++) {
1325                 centry_sid(centry, &(*sid_mem)[i]);
1326                 (*names)[i] = centry_string(centry, mem_ctx);
1327                 (*name_types)[i] = centry_uint32(centry);
1328         }
1329
1330 do_cached:      
1331         status = centry->status;
1332
1333         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
1334                 domain->name, get_friendly_nt_error_msg(status) ));
1335
1336         centry_free(centry);
1337         return status;
1338
1339 do_query:
1340         (*num_names) = 0;
1341         (*sid_mem) = NULL;
1342         (*names) = NULL;
1343         (*name_types) = NULL;
1344         
1345         /* Return status value returned by seq number check */
1346
1347         if (!NT_STATUS_IS_OK(domain->last_status))
1348                 return domain->last_status;
1349
1350         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
1351                 domain->name ));
1352
1353         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, 
1354                                                   sid_mem, names, name_types);
1355
1356         /* and save it */
1357         refresh_sequence_number(domain, False);
1358         centry = centry_start(domain, status);
1359         if (!centry)
1360                 goto skip_save;
1361         centry_put_uint32(centry, *num_names);
1362         for (i=0; i<(*num_names); i++) {
1363                 centry_put_sid(centry, &(*sid_mem)[i]);
1364                 centry_put_string(centry, (*names)[i]);
1365                 centry_put_uint32(centry, (*name_types)[i]);
1366         }       
1367         centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
1368         centry_free(centry);
1369
1370 skip_save:
1371         return status;
1372 }
1373
1374 /* find the sequence number for a domain */
1375 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1376 {
1377         refresh_sequence_number(domain, False);
1378
1379         *seq = domain->sequence_number;
1380
1381         return NT_STATUS_OK;
1382 }
1383
1384 /* enumerate trusted domains */
1385 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1386                                 TALLOC_CTX *mem_ctx,
1387                                 uint32 *num_domains,
1388                                 char ***names,
1389                                 char ***alt_names,
1390                                 DOM_SID **dom_sids)
1391 {
1392         get_cache(domain);
1393
1394         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
1395                 domain->name ));
1396
1397         /* we don't cache this call */
1398         return domain->backend->trusted_domains(domain, mem_ctx, num_domains, 
1399                                                names, alt_names, dom_sids);
1400 }
1401
1402 /* find the alternate names for the domain, if any */
1403 static NTSTATUS alternate_name(struct winbindd_domain *domain)
1404 {
1405         get_cache(domain);
1406
1407         DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
1408                 domain->name ));
1409
1410         /* we don't cache this call */
1411         return domain->backend->alternate_name(domain);
1412 }
1413
1414 /* Invalidate cached user and group lists coherently */
1415
1416 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
1417                        void *state)
1418 {
1419         if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
1420             strncmp(kbuf.dptr, "GL/", 3) == 0)
1421                 tdb_delete(the_tdb, kbuf);
1422
1423         return 0;
1424 }
1425
1426 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
1427
1428 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
1429                                 NET_USER_INFO_3 *info3)
1430 {
1431         struct winbind_cache *cache;
1432         
1433         if (!domain)
1434                 return;
1435
1436         cache = get_cache(domain);
1437         netsamlogon_clear_cached_user(cache->tdb, info3);
1438 }
1439
1440 void wcache_invalidate_cache(void)
1441 {
1442         struct winbindd_domain *domain;
1443
1444         for (domain = domain_list(); domain; domain = domain->next) {
1445                 struct winbind_cache *cache = get_cache(domain);
1446
1447                 DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
1448                            "entries for %s\n", domain->name));
1449                 if (cache)
1450                         tdb_traverse(cache->tdb, traverse_fn, NULL);
1451         }
1452 }
1453
1454 /* the ADS backend methods are exposed via this structure */
1455 struct winbindd_methods cache_methods = {
1456         True,
1457         query_user_list,
1458         enum_dom_groups,
1459         enum_local_groups,
1460         name_to_sid,
1461         sid_to_name,
1462         query_user,
1463         lookup_usergroups,
1464         lookup_useraliases,
1465         lookup_groupmem,
1466         sequence_number,
1467         trusted_domains,
1468         alternate_name
1469 };
1470
1471 static BOOL init_wcache(void)
1472 {
1473         if (wcache == NULL) {
1474                 wcache = SMB_XMALLOC_P(struct winbind_cache);
1475                 ZERO_STRUCTP(wcache);
1476         }
1477
1478         if (wcache->tdb != NULL)
1479                 return True;
1480
1481         wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
1482                                    TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
1483
1484         if (wcache->tdb == NULL) {
1485                 DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
1486                 return False;
1487         }
1488
1489         return True;
1490 }
1491
1492 void cache_store_response(pid_t pid, struct winbindd_response *response)
1493 {
1494         fstring key_str;
1495
1496         if (!init_wcache())
1497                 return;
1498
1499         DEBUG(10, ("Storing response for pid %d, len %d\n",
1500                    pid, response->length));
1501
1502         fstr_sprintf(key_str, "DR/%d", pid);
1503         if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
1504                       make_tdb_data((void *)response, sizeof(*response)),
1505                       TDB_REPLACE) == -1)
1506                 return;
1507
1508         if (response->length == sizeof(*response))
1509                 return;
1510
1511         /* There's extra data */
1512
1513         DEBUG(10, ("Storing extra data: len=%d\n",
1514                    response->length - sizeof(*response)));
1515
1516         fstr_sprintf(key_str, "DE/%d", pid);
1517         if (tdb_store(wcache->tdb, string_tdb_data(key_str),
1518                       make_tdb_data(response->extra_data,
1519                                     response->length - sizeof(*response)),
1520                       TDB_REPLACE) == 0)
1521                 return;
1522
1523         /* We could not store the extra data, make sure the tdb does not
1524          * contain a main record with wrong dangling extra data */
1525
1526         fstr_sprintf(key_str, "DR/%d", pid);
1527         tdb_delete(wcache->tdb, string_tdb_data(key_str));
1528
1529         return;
1530 }
1531
1532 BOOL cache_retrieve_response(pid_t pid, struct winbindd_response * response)
1533 {
1534         TDB_DATA data;
1535         fstring key_str;
1536
1537         if (!init_wcache())
1538                 return False;
1539
1540         DEBUG(10, ("Retrieving response for pid %d\n", pid));
1541
1542         fstr_sprintf(key_str, "DR/%d", pid);
1543         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1544
1545         if (data.dptr == NULL)
1546                 return False;
1547
1548         if (data.dsize != sizeof(*response))
1549                 return False;
1550
1551         memcpy(response, data.dptr, data.dsize);
1552         SAFE_FREE(data.dptr);
1553
1554         if (response->length == sizeof(*response)) {
1555                 response->extra_data = NULL;
1556                 return True;
1557         }
1558
1559         /* There's extra data */
1560
1561         DEBUG(10, ("Retrieving extra data length=%d\n",
1562                    response->length - sizeof(*response)));
1563
1564         fstr_sprintf(key_str, "DE/%d", pid);
1565         data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
1566
1567         if (data.dptr == NULL) {
1568                 DEBUG(0, ("Did not find extra data\n"));
1569                 return False;
1570         }
1571
1572         if (data.dsize != (response->length - sizeof(*response))) {
1573                 DEBUG(0, ("Invalid extra data length: %d\n", data.dsize));
1574                 SAFE_FREE(data.dptr);
1575                 return False;
1576         }
1577
1578         response->extra_data = data.dptr;
1579         return True;
1580 }
1581
1582 char *cache_store_request_data(TALLOC_CTX *mem_ctx, char *request_string)
1583 {
1584         int i;
1585
1586         if (!init_wcache())
1587                 return NULL;
1588
1589         for (i=0; i<2; i++) {
1590                 char *key = talloc_strdup(mem_ctx, generate_random_str(16));
1591                 if (key == NULL)
1592                         return NULL;
1593                 DEBUG(10, ("Storing request key %s\n", key));
1594                 if (tdb_store_bystring(wcache->tdb, key,
1595                                        string_tdb_data(request_string),
1596                                        TDB_INSERT) == 0)
1597                         return key;
1598         }
1599         return NULL;
1600 }
1601
1602 char *cache_retrieve_request_data(TALLOC_CTX *mem_ctx, char *key)
1603 {
1604         TDB_DATA data;
1605         char *result = NULL;
1606
1607         if (!init_wcache())
1608                 return NULL;
1609
1610         DEBUG(10, ("Retrieving key %s\n", key));
1611
1612         data = tdb_fetch_bystring(wcache->tdb, key);
1613         if (data.dptr == NULL)
1614                 return NULL;
1615
1616         if (strnlen(data.dptr, data.dsize) != (data.dsize)) {
1617                 DEBUG(0, ("Received invalid request string\n"));
1618                 goto done;
1619         }
1620         result = TALLOC_ARRAY(mem_ctx, char, data.dsize+1);
1621         if (result != NULL) {
1622                 memcpy(result, data.dptr, data.dsize);
1623                 result[data.dsize] = '\0';
1624         }
1625         if (tdb_delete_bystring(wcache->tdb, key) != 0) {
1626                 DEBUG(0, ("Could not delete key %s\n", key));
1627                 result = NULL;
1628         }
1629   done:
1630         SAFE_FREE(data.dptr);
1631         return result;
1632 }