winbind: Fix a typo in a wrong comment...
[kai/samba-autobuild/.git] / source3 / winbindd / winbindd_cache.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind cache backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Gerald Carter   2003-2007
8    Copyright (C) Volker Lendecke 2005
9    Copyright (C) Guenther Deschner 2005
10    Copyright (C) Michael Adam    2007
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "tdb_validate.h"
30 #include "../libcli/auth/libcli_auth.h"
31 #include "../librpc/gen_ndr/ndr_winbind.h"
32 #include "ads.h"
33 #include "nss_info.h"
34 #include "../libcli/security/security.h"
35 #include "passdb/machine_sid.h"
36 #include "util_tdb.h"
37
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_WINBIND
40
41 #define WINBINDD_CACHE_VER1 1 /* initial db version */
42 #define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
43
44 #define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
45 #define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
46
47 extern struct winbindd_methods reconnect_methods;
48 #ifdef HAVE_ADS
49 extern struct winbindd_methods reconnect_ads_methods;
50 #endif
51 extern struct winbindd_methods builtin_passdb_methods;
52 extern struct winbindd_methods sam_passdb_methods;
53
54 /*
55  * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
56  * Here are the list of entry types that are *not* stored
57  * as form struct cache_entry in the cache.
58  */
59
60 static const char *non_centry_keys[] = {
61         "SEQNUM/",
62         "WINBINDD_OFFLINE",
63         WINBINDD_CACHE_VERSION_KEYSTR,
64         NULL
65 };
66
67 /************************************************************************
68  Is this key a non-centry type ?
69 ************************************************************************/
70
71 static bool is_non_centry_key(TDB_DATA kbuf)
72 {
73         int i;
74
75         if (kbuf.dptr == NULL || kbuf.dsize == 0) {
76                 return false;
77         }
78         for (i = 0; non_centry_keys[i] != NULL; i++) {
79                 size_t namelen = strlen(non_centry_keys[i]);
80                 if (kbuf.dsize < namelen) {
81                         continue;
82                 }
83                 if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
84                         return true;
85                 }
86         }
87         return false;
88 }
89
90 /* Global online/offline state - False when online. winbindd starts up online
91    and sets this to true if the first query fails and there's an entry in
92    the cache tdb telling us to stay offline. */
93
94 static bool global_winbindd_offline_state;
95
96 struct winbind_cache {
97         TDB_CONTEXT *tdb;
98 };
99
100 struct cache_entry {
101         NTSTATUS status;
102         uint32_t sequence_number;
103         uint64_t timeout;
104         uint8_t *data;
105         uint32_t len, ofs;
106 };
107
108 void (*smb_panic_fn)(const char *const why) = smb_panic;
109
110 #define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
111
112 static struct winbind_cache *wcache;
113
114 /* get the winbind_cache structure */
115 static struct winbind_cache *get_cache(struct winbindd_domain *domain)
116 {
117         struct winbind_cache *ret = wcache;
118
119         /* We have to know what type of domain we are dealing with first. */
120
121         if (domain->internal) {
122                 domain->backend = &builtin_passdb_methods;
123         }
124
125         if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
126                 domain->initialized = true;
127         }
128
129         if (strequal(domain->name, get_global_sam_name()) &&
130             sid_check_is_our_sam(&domain->sid)) {
131                 domain->backend = &sam_passdb_methods;
132         }
133
134         if ( !domain->initialized ) {
135                 /* We do not need a connection to an RW DC for cache operation */
136                 init_dc_connection(domain, false);
137         }
138
139         /* 
140            OK.  Listen up because I'm only going to say this once.
141            We have the following scenarios to consider
142            (a) trusted AD domains on a Samba DC,
143            (b) trusted AD domains and we are joined to a non-kerberos domain
144            (c) trusted AD domains and we are joined to a kerberos (AD) domain
145
146            For (a) we can always contact the trusted domain using krb5 
147            since we have the domain trust account password
148
149            For (b) we can only use RPC since we have no way of 
150            getting a krb5 ticket in our own domain
151
152            For (c) we can always use krb5 since we have a kerberos trust
153
154            --jerry
155          */
156
157         if (!domain->backend) {
158 #ifdef HAVE_ADS
159                 struct winbindd_domain *our_domain = domain;
160
161                 /* find our domain first so we can figure out if we 
162                    are joined to a kerberized domain */
163
164                 if ( !domain->primary )
165                         our_domain = find_our_domain();
166
167                 if ((our_domain->active_directory || IS_DC)
168                     && domain->active_directory
169                     && !lp_winbind_rpc_only()) {
170                         DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
171                         domain->backend = &reconnect_ads_methods;
172                 } else {
173 #endif  /* HAVE_ADS */
174                         DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
175                         domain->backend = &reconnect_methods;
176 #ifdef HAVE_ADS
177                 }
178 #endif  /* HAVE_ADS */
179         }
180
181         if (ret)
182                 return ret;
183
184         ret = SMB_XMALLOC_P(struct winbind_cache);
185         ZERO_STRUCTP(ret);
186
187         wcache = ret;
188         wcache_flush_cache();
189
190         return ret;
191 }
192
193 /*
194   free a centry structure
195 */
196 static void centry_free(struct cache_entry *centry)
197 {
198         if (!centry)
199                 return;
200         SAFE_FREE(centry->data);
201         free(centry);
202 }
203
204 static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
205 {
206         if (centry->len - centry->ofs < nbytes) {
207                 DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
208                          (unsigned int)nbytes,
209                          centry->len - centry->ofs));
210                 return false;
211         }
212         return true;
213 }
214
215 /*
216   pull a uint64_t from a cache entry
217 */
218 static uint64_t centry_uint64_t(struct cache_entry *centry)
219 {
220         uint64_t ret;
221
222         if (!centry_check_bytes(centry, 8)) {
223                 smb_panic_fn("centry_uint64_t");
224         }
225         ret = BVAL(centry->data, centry->ofs);
226         centry->ofs += 8;
227         return ret;
228 }
229
230 /*
231   pull a uint32_t from a cache entry
232 */
233 static uint32_t centry_uint32(struct cache_entry *centry)
234 {
235         uint32_t ret;
236
237         if (!centry_check_bytes(centry, 4)) {
238                 smb_panic_fn("centry_uint32");
239         }
240         ret = IVAL(centry->data, centry->ofs);
241         centry->ofs += 4;
242         return ret;
243 }
244
245 /*
246   pull a uint16_t from a cache entry
247 */
248 static uint16_t centry_uint16(struct cache_entry *centry)
249 {
250         uint16_t ret;
251         if (!centry_check_bytes(centry, 2)) {
252                 smb_panic_fn("centry_uint16");
253         }
254         ret = SVAL(centry->data, centry->ofs);
255         centry->ofs += 2;
256         return ret;
257 }
258
259 /*
260   pull a uint8_t from a cache entry
261 */
262 static uint8_t centry_uint8(struct cache_entry *centry)
263 {
264         uint8_t ret;
265         if (!centry_check_bytes(centry, 1)) {
266                 smb_panic_fn("centry_uint8");
267         }
268         ret = CVAL(centry->data, centry->ofs);
269         centry->ofs += 1;
270         return ret;
271 }
272
273 /*
274   pull a NTTIME from a cache entry 
275 */
276 static NTTIME centry_nttime(struct cache_entry *centry)
277 {
278         NTTIME ret;
279         if (!centry_check_bytes(centry, 8)) {
280                 smb_panic_fn("centry_nttime");
281         }
282         ret = IVAL(centry->data, centry->ofs);
283         centry->ofs += 4;
284         ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
285         centry->ofs += 4;
286         return ret;
287 }
288
289 /*
290   pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
291 */
292 static time_t centry_time(struct cache_entry *centry)
293 {
294         return (time_t)centry_nttime(centry);
295 }
296
297 /* pull a string from a cache entry, using the supplied
298    talloc context 
299 */
300 static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
301 {
302         uint32_t len;
303         char *ret;
304
305         len = centry_uint8(centry);
306
307         if (len == 0xFF) {
308                 /* a deliberate NULL string */
309                 return NULL;
310         }
311
312         if (!centry_check_bytes(centry, (size_t)len)) {
313                 smb_panic_fn("centry_string");
314         }
315
316         ret = talloc_array(mem_ctx, char, len+1);
317         if (!ret) {
318                 smb_panic_fn("centry_string out of memory\n");
319         }
320         memcpy(ret,centry->data + centry->ofs, len);
321         ret[len] = 0;
322         centry->ofs += len;
323         return ret;
324 }
325
326 /* pull a hash16 from a cache entry, using the supplied
327    talloc context 
328 */
329 static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
330 {
331         uint32_t len;
332         char *ret;
333
334         len = centry_uint8(centry);
335
336         if (len != 16) {
337                 DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
338                         len ));
339                 return NULL;
340         }
341
342         if (!centry_check_bytes(centry, 16)) {
343                 return NULL;
344         }
345
346         ret = talloc_array(mem_ctx, char, 16);
347         if (!ret) {
348                 smb_panic_fn("centry_hash out of memory\n");
349         }
350         memcpy(ret,centry->data + centry->ofs, 16);
351         centry->ofs += 16;
352         return ret;
353 }
354
355 /* pull a sid from a cache entry, using the supplied
356    talloc context 
357 */
358 static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
359 {
360         char *sid_string;
361         bool ret;
362
363         sid_string = centry_string(centry, talloc_tos());
364         if (sid_string == NULL) {
365                 return false;
366         }
367         ret = string_to_sid(sid, sid_string);
368         TALLOC_FREE(sid_string);
369         return ret;
370 }
371
372
373 /*
374   pull a NTSTATUS from a cache entry
375 */
376 static NTSTATUS centry_ntstatus(struct cache_entry *centry)
377 {
378         NTSTATUS status;
379
380         status = NT_STATUS(centry_uint32(centry));
381         return status;
382 }
383
384
385 /* the server is considered down if it can't give us a sequence number */
386 static bool wcache_server_down(struct winbindd_domain *domain)
387 {
388         bool ret;
389
390         if (!wcache->tdb)
391                 return false;
392
393         ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
394
395         if (ret)
396                 DEBUG(10,("wcache_server_down: server for Domain %s down\n", 
397                         domain->name ));
398         return ret;
399 }
400
401 struct wcache_seqnum_state {
402         uint32_t *seqnum;
403         uint32_t *last_seq_check;
404 };
405
406 static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
407                                 void *private_data)
408 {
409         struct wcache_seqnum_state *state = private_data;
410
411         if (data.dsize != 8) {
412                 DEBUG(10, ("wcache_fetch_seqnum: invalid data size %d\n",
413                            (int)data.dsize));
414                 return -1;
415         }
416
417         *state->seqnum = IVAL(data.dptr, 0);
418         *state->last_seq_check = IVAL(data.dptr, 4);
419         return 0;
420 }
421
422 static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
423                                 uint32_t *last_seq_check)
424 {
425         struct wcache_seqnum_state state = {
426                 .seqnum = seqnum, .last_seq_check = last_seq_check
427         };
428         size_t len = strlen(domain_name);
429         char keystr[len+8];
430         TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
431         int ret;
432
433         if (wcache->tdb == NULL) {
434                 DEBUG(10,("wcache_fetch_seqnum: tdb == NULL\n"));
435                 return false;
436         }
437
438         snprintf(keystr, sizeof(keystr),  "SEQNUM/%s", domain_name);
439
440         ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
441                                &state);
442         return (ret == 0);
443 }
444
445 static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
446 {
447         uint32_t last_check, time_diff;
448
449         if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
450                                  &last_check)) {
451                 return NT_STATUS_UNSUCCESSFUL;
452         }
453         domain->last_seq_check = last_check;
454
455         /* have we expired? */
456
457         time_diff = now - domain->last_seq_check;
458         if ( time_diff > lp_winbind_cache_time() ) {
459                 DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
460                         domain->name, domain->sequence_number,
461                         (uint32_t)domain->last_seq_check));
462                 return NT_STATUS_UNSUCCESSFUL;
463         }
464
465         DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n", 
466                 domain->name, domain->sequence_number, 
467                 (uint32_t)domain->last_seq_check));
468
469         return NT_STATUS_OK;
470 }
471
472 bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
473                          time_t last_seq_check)
474 {
475         size_t len = strlen(domain_name);
476         char keystr[len+8];
477         TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
478         uint8_t buf[8];
479         int ret;
480
481         if (wcache->tdb == NULL) {
482                 DEBUG(10, ("wcache_store_seqnum: wcache->tdb == NULL\n"));
483                 return false;
484         }
485
486         snprintf(keystr, sizeof(keystr),  "SEQNUM/%s", domain_name);
487
488         SIVAL(buf, 0, seqnum);
489         SIVAL(buf, 4, last_seq_check);
490
491         ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
492                         TDB_REPLACE);
493         if (ret != 0) {
494                 DEBUG(10, ("tdb_store_bystring failed: %s\n",
495                            tdb_errorstr(wcache->tdb)));
496                 return false;
497         }
498
499         DEBUG(10, ("wcache_store_seqnum: success [%s][%u @ %u]\n",
500                    domain_name, seqnum, (unsigned)last_seq_check));
501
502         return true;
503 }
504
505 static bool store_cache_seqnum( struct winbindd_domain *domain )
506 {
507         return wcache_store_seqnum(domain->name, domain->sequence_number,
508                                    domain->last_seq_check);
509 }
510
511 /*
512   refresh the domain sequence number. If force is true
513   then always refresh it, no matter how recently we fetched it
514 */
515
516 static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
517 {
518         NTSTATUS status;
519         unsigned time_diff;
520         time_t t = time(NULL);
521         unsigned cache_time = lp_winbind_cache_time();
522
523         if (is_domain_offline(domain)) {
524                 return;
525         }
526
527         get_cache( domain );
528
529 #if 0   /* JERRY -- disable as the default cache time is now 5 minutes */
530         /* trying to reconnect is expensive, don't do it too often */
531         if (domain->sequence_number == DOM_SEQUENCE_NONE) {
532                 cache_time *= 8;
533         }
534 #endif
535
536         time_diff = t - domain->last_seq_check;
537
538         /* see if we have to refetch the domain sequence number */
539         if (!force && (time_diff < cache_time) &&
540                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
541                         NT_STATUS_IS_OK(domain->last_status)) {
542                 DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
543                 goto done;
544         }
545
546         /* try to get the sequence number from the tdb cache first */
547         /* this will update the timestamp as well */
548
549         status = fetch_cache_seqnum( domain, t );
550         if (NT_STATUS_IS_OK(status) &&
551                         (domain->sequence_number != DOM_SEQUENCE_NONE) &&
552                         NT_STATUS_IS_OK(domain->last_status)) {
553                 goto done;
554         }
555
556         /* important! make sure that we know if this is a native 
557            mode domain or not.  And that we can contact it. */
558
559         if ( winbindd_can_contact_domain( domain ) ) {          
560                 status = domain->backend->sequence_number(domain, 
561                                                           &domain->sequence_number);
562         } else {
563                 /* just use the current time */
564                 status = NT_STATUS_OK;
565                 domain->sequence_number = time(NULL);
566         }
567
568
569         /* the above call could have set our domain->backend to NULL when
570          * coming from offline to online mode, make sure to reinitialize the
571          * backend - Guenther */
572         get_cache( domain );
573
574         if (!NT_STATUS_IS_OK(status)) {
575                 DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
576                 domain->sequence_number = DOM_SEQUENCE_NONE;
577         }
578
579         domain->last_status = status;
580         domain->last_seq_check = time(NULL);
581
582         /* save the new sequence number in the cache */
583         store_cache_seqnum( domain );
584
585 done:
586         DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n", 
587                    domain->name, domain->sequence_number));
588
589         return;
590 }
591
592 /*
593   decide if a cache entry has expired
594 */
595 static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
596 {
597         /* If we've been told to be offline - stay in that state... */
598         if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
599                 DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
600                         keystr, domain->name ));
601                 return false;
602         }
603
604         /* when the domain is offline return the cached entry.
605          * This deals with transient offline states... */
606
607         if (!domain->online) {
608                 DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
609                         keystr, domain->name ));
610                 return false;
611         }
612
613         /* if the server is OK and our cache entry came from when it was down then
614            the entry is invalid */
615         if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&  
616             (centry->sequence_number == DOM_SEQUENCE_NONE)) {
617                 DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
618                         keystr, domain->name ));
619                 return true;
620         }
621
622         /* if the server is down or the cache entry is not older than the
623            current sequence number or it did not timeout then it is OK */
624         if (wcache_server_down(domain)
625             || ((centry->sequence_number == domain->sequence_number)
626                 && (centry->timeout > time(NULL)))) {
627                 DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
628                         keystr, domain->name ));
629                 return false;
630         }
631
632         DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
633                 keystr, domain->name ));
634
635         /* it's expired */
636         return true;
637 }
638
639 static struct cache_entry *wcache_fetch_raw(char *kstr)
640 {
641         TDB_DATA data;
642         struct cache_entry *centry;
643         TDB_DATA key;
644
645         key = string_tdb_data(kstr);
646         data = tdb_fetch(wcache->tdb, key);
647         if (!data.dptr) {
648                 /* a cache miss */
649                 return NULL;
650         }
651
652         centry = SMB_XMALLOC_P(struct cache_entry);
653         centry->data = (unsigned char *)data.dptr;
654         centry->len = data.dsize;
655         centry->ofs = 0;
656
657         if (centry->len < 16) {
658                 /* huh? corrupt cache? */
659                 DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s "
660                           "(len < 16)?\n", kstr));
661                 centry_free(centry);
662                 return NULL;
663         }
664
665         centry->status = centry_ntstatus(centry);
666         centry->sequence_number = centry_uint32(centry);
667         centry->timeout = centry_uint64_t(centry);
668
669         return centry;
670 }
671
672 static bool is_my_own_sam_domain(struct winbindd_domain *domain)
673 {
674         if (strequal(domain->name, get_global_sam_name()) &&
675             sid_check_is_our_sam(&domain->sid)) {
676                 return true;
677         }
678
679         return false;
680 }
681
682 static bool is_builtin_domain(struct winbindd_domain *domain)
683 {
684         if (strequal(domain->name, "BUILTIN") &&
685             sid_check_is_builtin(&domain->sid)) {
686                 return true;
687         }
688
689         return false;
690 }
691
692 /*
693   fetch an entry from the cache, with a varargs key. auto-fetch the sequence
694   number and return status
695 */
696 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
697                                         struct winbindd_domain *domain,
698                                         const char *format, ...) PRINTF_ATTRIBUTE(3,4);
699 static struct cache_entry *wcache_fetch(struct winbind_cache *cache, 
700                                         struct winbindd_domain *domain,
701                                         const char *format, ...)
702 {
703         va_list ap;
704         char *kstr;
705         struct cache_entry *centry;
706
707         if (!winbindd_use_cache() ||
708             is_my_own_sam_domain(domain) ||
709             is_builtin_domain(domain)) {
710                 return NULL;
711         }
712
713         refresh_sequence_number(domain, false);
714
715         va_start(ap, format);
716         smb_xvasprintf(&kstr, format, ap);
717         va_end(ap);
718
719         centry = wcache_fetch_raw(kstr);
720         if (centry == NULL) {
721                 free(kstr);
722                 return NULL;
723         }
724
725         if (centry_expired(domain, kstr, centry)) {
726
727                 DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
728                          kstr, domain->name ));
729
730                 centry_free(centry);
731                 free(kstr);
732                 return NULL;
733         }
734
735         DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
736                  kstr, domain->name ));
737
738         free(kstr);
739         return centry;
740 }
741
742 static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
743 static void wcache_delete(const char *format, ...)
744 {
745         va_list ap;
746         char *kstr;
747         TDB_DATA key;
748
749         va_start(ap, format);
750         smb_xvasprintf(&kstr, format, ap);
751         va_end(ap);
752
753         key = string_tdb_data(kstr);
754
755         tdb_delete(wcache->tdb, key);
756         free(kstr);
757 }
758
759 /*
760   make sure we have at least len bytes available in a centry 
761 */
762 static void centry_expand(struct cache_entry *centry, uint32_t len)
763 {
764         if (centry->len - centry->ofs >= len)
765                 return;
766         centry->len *= 2;
767         centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
768                                          centry->len);
769         if (!centry->data) {
770                 DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
771                 smb_panic_fn("out of memory in centry_expand");
772         }
773 }
774
775 /*
776   push a uint64_t into a centry
777 */
778 static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
779 {
780         centry_expand(centry, 8);
781         SBVAL(centry->data, centry->ofs, v);
782         centry->ofs += 8;
783 }
784
785 /*
786   push a uint32_t into a centry
787 */
788 static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
789 {
790         centry_expand(centry, 4);
791         SIVAL(centry->data, centry->ofs, v);
792         centry->ofs += 4;
793 }
794
795 /*
796   push a uint16_t into a centry
797 */
798 static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
799 {
800         centry_expand(centry, 2);
801         SSVAL(centry->data, centry->ofs, v);
802         centry->ofs += 2;
803 }
804
805 /*
806   push a uint8_t into a centry
807 */
808 static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
809 {
810         centry_expand(centry, 1);
811         SCVAL(centry->data, centry->ofs, v);
812         centry->ofs += 1;
813 }
814
815 /* 
816    push a string into a centry 
817  */
818 static void centry_put_string(struct cache_entry *centry, const char *s)
819 {
820         int len;
821
822         if (!s) {
823                 /* null strings are marked as len 0xFFFF */
824                 centry_put_uint8(centry, 0xFF);
825                 return;
826         }
827
828         len = strlen(s);
829         /* can't handle more than 254 char strings. Truncating is probably best */
830         if (len > 254) {
831                 DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
832                 len = 254;
833         }
834         centry_put_uint8(centry, len);
835         centry_expand(centry, len);
836         memcpy(centry->data + centry->ofs, s, len);
837         centry->ofs += len;
838 }
839
840 /* 
841    push a 16 byte hash into a centry - treat as 16 byte string.
842  */
843 static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
844 {
845         centry_put_uint8(centry, 16);
846         centry_expand(centry, 16);
847         memcpy(centry->data + centry->ofs, val, 16);
848         centry->ofs += 16;
849 }
850
851 static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
852 {
853         fstring sid_string;
854         centry_put_string(centry, sid_to_fstring(sid_string, sid));
855 }
856
857
858 /*
859   put NTSTATUS into a centry
860 */
861 static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
862 {
863         uint32_t status_value = NT_STATUS_V(status);
864         centry_put_uint32(centry, status_value);
865 }
866
867
868 /*
869   push a NTTIME into a centry 
870 */
871 static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
872 {
873         centry_expand(centry, 8);
874         SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
875         centry->ofs += 4;
876         SIVAL(centry->data, centry->ofs, nt >> 32);
877         centry->ofs += 4;
878 }
879
880 /*
881   push a time_t into a centry - use a 64 bit size.
882   NTTIME here is being used as a convenient 64-bit size.
883 */
884 static void centry_put_time(struct cache_entry *centry, time_t t)
885 {
886         NTTIME nt = (NTTIME)t;
887         centry_put_nttime(centry, nt);
888 }
889
890 /*
891   start a centry for output. When finished, call centry_end()
892 */
893 static struct cache_entry *centry_start(struct winbindd_domain *domain,
894                                         NTSTATUS status)
895 {
896         struct cache_entry *centry;
897
898         if (!wcache->tdb)
899                 return NULL;
900
901         centry = SMB_XMALLOC_P(struct cache_entry);
902
903         centry->len = 8192; /* reasonable default */
904         centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
905         centry->ofs = 0;
906         centry->sequence_number = domain->sequence_number;
907         centry->timeout = lp_winbind_cache_time() + time(NULL);
908         centry_put_ntstatus(centry, status);
909         centry_put_uint32(centry, centry->sequence_number);
910         centry_put_uint64_t(centry, centry->timeout);
911         return centry;
912 }
913
914 /*
915   finish a centry and write it to the tdb
916 */
917 static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
918 static void centry_end(struct cache_entry *centry, const char *format, ...)
919 {
920         va_list ap;
921         char *kstr;
922         TDB_DATA key, data;
923
924         if (!winbindd_use_cache()) {
925                 return;
926         }
927
928         va_start(ap, format);
929         smb_xvasprintf(&kstr, format, ap);
930         va_end(ap);
931
932         key = string_tdb_data(kstr);
933         data.dptr = centry->data;
934         data.dsize = centry->ofs;
935
936         tdb_store(wcache->tdb, key, data, TDB_REPLACE);
937         free(kstr);
938 }
939
940 static void wcache_save_name_to_sid(struct winbindd_domain *domain, 
941                                     NTSTATUS status, const char *domain_name,
942                                     const char *name, const struct dom_sid *sid,
943                                     enum lsa_SidType type)
944 {
945         struct cache_entry *centry;
946         fstring uname;
947
948         centry = centry_start(domain, status);
949         if (!centry)
950                 return;
951
952         if ((domain_name == NULL) || (domain_name[0] == '\0')) {
953                 struct winbindd_domain *mydomain =
954                         find_domain_from_sid_noinit(sid);
955                 if (mydomain != NULL) {
956                         domain_name = mydomain->name;
957                 }
958         }
959
960         centry_put_uint32(centry, type);
961         centry_put_sid(centry, sid);
962         fstrcpy(uname, name);
963         (void)strupper_m(uname);
964         centry_end(centry, "NS/%s/%s", domain_name, uname);
965         DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
966                   uname, sid_string_dbg(sid), nt_errstr(status)));
967         centry_free(centry);
968 }
969
970 static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
971                                     const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
972 {
973         struct cache_entry *centry;
974         fstring sid_string;
975
976         centry = centry_start(domain, status);
977         if (!centry)
978                 return;
979
980         if ((domain_name == NULL) || (domain_name[0] == '\0')) {
981                 struct winbindd_domain *mydomain =
982                         find_domain_from_sid_noinit(sid);
983                 if (mydomain != NULL) {
984                         domain_name = mydomain->name;
985                 }
986         }
987
988         if (NT_STATUS_IS_OK(status)) {
989                 centry_put_uint32(centry, type);
990                 centry_put_string(centry, domain_name);
991                 centry_put_string(centry, name);
992         }
993
994         centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
995         DEBUG(10,("wcache_save_sid_to_name: %s -> %s\\%s (%s)\n", sid_string,
996                   domain_name, name, nt_errstr(status)));
997         centry_free(centry);
998 }
999
1000
1001 static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
1002                              struct wbint_userinfo *info)
1003 {
1004         struct cache_entry *centry;
1005         fstring sid_string;
1006
1007         if (is_null_sid(&info->user_sid)) {
1008                 return;
1009         }
1010
1011         centry = centry_start(domain, status);
1012         if (!centry)
1013                 return;
1014         centry_put_string(centry, info->acct_name);
1015         centry_put_string(centry, info->full_name);
1016         centry_put_string(centry, info->homedir);
1017         centry_put_string(centry, info->shell);
1018         centry_put_uint32(centry, info->primary_gid);
1019         centry_put_sid(centry, &info->user_sid);
1020         centry_put_sid(centry, &info->group_sid);
1021         centry_end(centry, "U/%s", sid_to_fstring(sid_string,
1022                                                   &info->user_sid));
1023         DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
1024         centry_free(centry);
1025 }
1026
1027 static void wcache_save_lockout_policy(struct winbindd_domain *domain,
1028                                        NTSTATUS status,
1029                                        struct samr_DomInfo12 *lockout_policy)
1030 {
1031         struct cache_entry *centry;
1032
1033         centry = centry_start(domain, status);
1034         if (!centry)
1035                 return;
1036
1037         centry_put_nttime(centry, lockout_policy->lockout_duration);
1038         centry_put_nttime(centry, lockout_policy->lockout_window);
1039         centry_put_uint16(centry, lockout_policy->lockout_threshold);
1040
1041         centry_end(centry, "LOC_POL/%s", domain->name);
1042
1043         DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
1044
1045         centry_free(centry);
1046 }
1047
1048
1049
1050 static void wcache_save_password_policy(struct winbindd_domain *domain,
1051                                         NTSTATUS status,
1052                                         struct samr_DomInfo1 *policy)
1053 {
1054         struct cache_entry *centry;
1055
1056         centry = centry_start(domain, status);
1057         if (!centry)
1058                 return;
1059
1060         centry_put_uint16(centry, policy->min_password_length);
1061         centry_put_uint16(centry, policy->password_history_length);
1062         centry_put_uint32(centry, policy->password_properties);
1063         centry_put_nttime(centry, policy->max_password_age);
1064         centry_put_nttime(centry, policy->min_password_age);
1065
1066         centry_end(centry, "PWD_POL/%s", domain->name);
1067
1068         DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
1069
1070         centry_free(centry);
1071 }
1072
1073 /***************************************************************************
1074  ***************************************************************************/
1075
1076 static void wcache_save_username_alias(struct winbindd_domain *domain,
1077                                        NTSTATUS status,
1078                                        const char *name, const char *alias)
1079 {
1080         struct cache_entry *centry;
1081         fstring uname;
1082
1083         if ( (centry = centry_start(domain, status)) == NULL )
1084                 return;
1085
1086         centry_put_string( centry, alias );
1087
1088         fstrcpy(uname, name);
1089         (void)strupper_m(uname);
1090         centry_end(centry, "NSS/NA/%s", uname);
1091
1092         DEBUG(10,("wcache_save_username_alias: %s -> %s\n", name, alias ));
1093
1094         centry_free(centry);
1095 }
1096
1097 static void wcache_save_alias_username(struct winbindd_domain *domain,
1098                                        NTSTATUS status,
1099                                        const char *alias, const char *name)
1100 {
1101         struct cache_entry *centry;
1102         fstring uname;
1103
1104         if ( (centry = centry_start(domain, status)) == NULL )
1105                 return;
1106
1107         centry_put_string( centry, name );
1108
1109         fstrcpy(uname, alias);
1110         (void)strupper_m(uname);
1111         centry_end(centry, "NSS/AN/%s", uname);
1112
1113         DEBUG(10,("wcache_save_alias_username: %s -> %s\n", alias, name ));
1114
1115         centry_free(centry);
1116 }
1117
1118 /***************************************************************************
1119  ***************************************************************************/
1120
1121 NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
1122                                     struct winbindd_domain *domain,
1123                                     const char *name, char **alias )
1124 {
1125         struct winbind_cache *cache = get_cache(domain);
1126         struct cache_entry *centry = NULL;
1127         NTSTATUS status;
1128         char *upper_name;
1129
1130         if ( domain->internal )
1131                 return NT_STATUS_NOT_SUPPORTED;
1132
1133         if (!cache->tdb)
1134                 goto do_query;
1135
1136         upper_name = talloc_strdup(mem_ctx, name);
1137         if (upper_name == NULL) {
1138                 return NT_STATUS_NO_MEMORY;
1139         }
1140         if (!strupper_m(upper_name)) {
1141                 talloc_free(upper_name);
1142                 return NT_STATUS_INVALID_PARAMETER;
1143         }
1144
1145         centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
1146
1147         talloc_free(upper_name);
1148
1149         if (!centry)
1150                 goto do_query;
1151
1152         status = centry->status;
1153
1154         if (!NT_STATUS_IS_OK(status)) {
1155                 centry_free(centry);
1156                 return status;
1157         }
1158
1159         *alias = centry_string( centry, mem_ctx );
1160
1161         centry_free(centry);
1162
1163         DEBUG(10,("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
1164                   name, *alias ? *alias : "(none)"));
1165
1166         return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1167
1168 do_query:
1169
1170         /* If its not in cache and we are offline, then fail */
1171
1172         if ( get_global_winbindd_state_offline() || !domain->online ) {
1173                 DEBUG(8,("resolve_username_to_alias: rejecting query "
1174                          "in offline mode\n"));
1175                 return NT_STATUS_NOT_FOUND;
1176         }
1177
1178         status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
1179
1180         if ( NT_STATUS_IS_OK( status ) ) {
1181                 wcache_save_username_alias(domain, status, name, *alias);
1182         }
1183
1184         if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
1185                 wcache_save_username_alias(domain, status, name, "(NULL)");
1186         }
1187
1188         DEBUG(5,("resolve_username_to_alias: backend query returned %s\n",
1189                  nt_errstr(status)));
1190
1191         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1192                 set_domain_offline( domain );
1193         }
1194
1195         return status;
1196 }
1197
1198 /***************************************************************************
1199  ***************************************************************************/
1200
1201 NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
1202                                     struct winbindd_domain *domain,
1203                                     const char *alias, char **name )
1204 {
1205         struct winbind_cache *cache = get_cache(domain);
1206         struct cache_entry *centry = NULL;
1207         NTSTATUS status;
1208         char *upper_name;
1209
1210         if ( domain->internal )
1211                 return  NT_STATUS_NOT_SUPPORTED;
1212
1213         if (!cache->tdb)
1214                 goto do_query;
1215
1216         upper_name = talloc_strdup(mem_ctx, alias);
1217         if (upper_name == NULL) {
1218                 return NT_STATUS_NO_MEMORY;
1219         }
1220         if (!strupper_m(upper_name)) {
1221                 talloc_free(upper_name);
1222                 return NT_STATUS_INVALID_PARAMETER;
1223         }
1224
1225         centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
1226
1227         talloc_free(upper_name);
1228
1229         if (!centry)
1230                 goto do_query;
1231
1232         status = centry->status;
1233
1234         if (!NT_STATUS_IS_OK(status)) {
1235                 centry_free(centry);
1236                 return status;
1237         }
1238
1239         *name = centry_string( centry, mem_ctx );
1240
1241         centry_free(centry);
1242
1243         DEBUG(10,("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
1244                   alias, *name ? *name : "(none)"));
1245
1246         return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
1247
1248 do_query:
1249
1250         /* If its not in cache and we are offline, then fail */
1251
1252         if ( get_global_winbindd_state_offline() || !domain->online ) {
1253                 DEBUG(8,("resolve_alias_to_username: rejecting query "
1254                          "in offline mode\n"));
1255                 return NT_STATUS_NOT_FOUND;
1256         }
1257
1258         /* an alias cannot contain a domain prefix or '@' */
1259
1260         if (strchr(alias, '\\') || strchr(alias, '@')) {
1261                 DEBUG(10,("resolve_alias_to_username: skipping fully "
1262                           "qualified name %s\n", alias));
1263                 return NT_STATUS_OBJECT_NAME_INVALID;
1264         }
1265
1266         status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
1267
1268         if ( NT_STATUS_IS_OK( status ) ) {
1269                 wcache_save_alias_username( domain, status, alias, *name );
1270         }
1271
1272         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1273                 wcache_save_alias_username(domain, status, alias, "(NULL)");
1274         }
1275
1276         DEBUG(5,("resolve_alias_to_username: backend query returned %s\n",
1277                  nt_errstr(status)));
1278
1279         if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
1280                 set_domain_offline( domain );
1281         }
1282
1283         return status;
1284 }
1285
1286 NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
1287 {
1288         struct winbind_cache *cache = get_cache(domain);
1289         TDB_DATA data;
1290         fstring key_str, tmp;
1291         uint32_t rid;
1292
1293         if (!cache->tdb) {
1294                 return NT_STATUS_INTERNAL_DB_ERROR;
1295         }
1296
1297         if (is_null_sid(sid)) {
1298                 return NT_STATUS_INVALID_SID;
1299         }
1300
1301         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1302                 return NT_STATUS_INVALID_SID;
1303         }
1304
1305         fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
1306
1307         data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
1308         if (!data.dptr) {
1309                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1310         }
1311
1312         SAFE_FREE(data.dptr);
1313         return NT_STATUS_OK;
1314 }
1315
1316 /* Lookup creds for a SID - copes with old (unsalted) creds as well
1317    as new salted ones. */
1318
1319 NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
1320                           TALLOC_CTX *mem_ctx, 
1321                           const struct dom_sid *sid,
1322                           const uint8_t **cached_nt_pass,
1323                           const uint8_t **cached_salt)
1324 {
1325         struct winbind_cache *cache = get_cache(domain);
1326         struct cache_entry *centry = NULL;
1327         NTSTATUS status;
1328         uint32_t rid;
1329         fstring tmp;
1330
1331         if (!cache->tdb) {
1332                 return NT_STATUS_INTERNAL_DB_ERROR;
1333         }
1334
1335         if (is_null_sid(sid)) {
1336                 return NT_STATUS_INVALID_SID;
1337         }
1338
1339         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1340                 return NT_STATUS_INVALID_SID;
1341         }
1342
1343         /* Try and get a salted cred first. If we can't
1344            fall back to an unsalted cred. */
1345
1346         centry = wcache_fetch(cache, domain, "CRED/%s",
1347                               sid_to_fstring(tmp, sid));
1348         if (!centry) {
1349                 DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
1350                           sid_string_dbg(sid)));
1351                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1352         }
1353
1354         /*
1355          * We don't use the time element at this moment,
1356          * but we have to consume it, so that we don't
1357          * neet to change the disk format of the cache.
1358          */
1359         (void)centry_time(centry);
1360
1361         /* In the salted case this isn't actually the nt_hash itself,
1362            but the MD5 of the salt + nt_hash. Let the caller
1363            sort this out. It can tell as we only return the cached_salt
1364            if we are returning a salted cred. */
1365
1366         *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
1367         if (*cached_nt_pass == NULL) {
1368                 fstring sidstr;
1369
1370                 sid_to_fstring(sidstr, sid);
1371
1372                 /* Bad (old) cred cache. Delete and pretend we
1373                    don't have it. */
1374                 DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
1375                                 sidstr));
1376                 wcache_delete("CRED/%s", sidstr);
1377                 centry_free(centry);
1378                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1379         }
1380
1381         /* We only have 17 bytes more data in the salted cred case. */
1382         if (centry->len - centry->ofs == 17) {
1383                 *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
1384         } else {
1385                 *cached_salt = NULL;
1386         }
1387
1388         dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
1389         if (*cached_salt) {
1390                 dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
1391         }
1392
1393         status = centry->status;
1394
1395         DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
1396                   sid_string_dbg(sid), nt_errstr(status) ));
1397
1398         centry_free(centry);
1399         return status;
1400 }
1401
1402 /* Store creds for a SID - only writes out new salted ones. */
1403
1404 NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
1405                            const struct dom_sid *sid,
1406                            const uint8_t nt_pass[NT_HASH_LEN])
1407 {
1408         struct cache_entry *centry;
1409         fstring sid_string;
1410         uint32_t rid;
1411         uint8_t cred_salt[NT_HASH_LEN];
1412         uint8_t salted_hash[NT_HASH_LEN];
1413
1414         if (is_null_sid(sid)) {
1415                 return NT_STATUS_INVALID_SID;
1416         }
1417
1418         if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
1419                 return NT_STATUS_INVALID_SID;
1420         }
1421
1422         centry = centry_start(domain, NT_STATUS_OK);
1423         if (!centry) {
1424                 return NT_STATUS_INTERNAL_DB_ERROR;
1425         }
1426
1427         dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
1428
1429         centry_put_time(centry, time(NULL));
1430
1431         /* Create a salt and then salt the hash. */
1432         generate_random_buffer(cred_salt, NT_HASH_LEN);
1433         E_md5hash(cred_salt, nt_pass, salted_hash);
1434
1435         centry_put_hash16(centry, salted_hash);
1436         centry_put_hash16(centry, cred_salt);
1437         centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
1438
1439         DEBUG(10,("wcache_save_creds: %s\n", sid_string));
1440
1441         centry_free(centry);
1442
1443         return NT_STATUS_OK;
1444 }
1445
1446
1447 /* Query display info. This is the basic user list fn */
1448 static NTSTATUS query_user_list(struct winbindd_domain *domain,
1449                                 TALLOC_CTX *mem_ctx,
1450                                 uint32_t *num_entries,
1451                                 struct wbint_userinfo **info)
1452 {
1453         struct winbind_cache *cache = get_cache(domain);
1454         struct cache_entry *centry = NULL;
1455         NTSTATUS status;
1456         unsigned int i, retry;
1457         bool old_status = domain->online;
1458
1459         if (!cache->tdb)
1460                 goto do_query;
1461
1462         centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1463         if (!centry)
1464                 goto do_query;
1465
1466 do_fetch_cache:
1467         *num_entries = centry_uint32(centry);
1468
1469         if (*num_entries == 0)
1470                 goto do_cached;
1471
1472         (*info) = talloc_array(mem_ctx, struct wbint_userinfo, *num_entries);
1473         if (! (*info)) {
1474                 smb_panic_fn("query_user_list out of memory");
1475         }
1476         for (i=0; i<(*num_entries); i++) {
1477                 (*info)[i].acct_name = centry_string(centry, mem_ctx);
1478                 (*info)[i].full_name = centry_string(centry, mem_ctx);
1479                 (*info)[i].homedir = centry_string(centry, mem_ctx);
1480                 (*info)[i].shell = centry_string(centry, mem_ctx);
1481                 centry_sid(centry, &(*info)[i].user_sid);
1482                 centry_sid(centry, &(*info)[i].group_sid);
1483         }
1484
1485 do_cached:      
1486         status = centry->status;
1487
1488         DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
1489                 domain->name, nt_errstr(status) ));
1490
1491         centry_free(centry);
1492         return status;
1493
1494 do_query:
1495         *num_entries = 0;
1496         *info = NULL;
1497
1498         /* Return status value returned by seq number check */
1499
1500         if (!NT_STATUS_IS_OK(domain->last_status))
1501                 return domain->last_status;
1502
1503         /* Put the query_user_list() in a retry loop.  There appears to be
1504          * some bug either with Windows 2000 or Samba's handling of large
1505          * rpc replies.  This manifests itself as sudden disconnection
1506          * at a random point in the enumeration of a large (60k) user list.
1507          * The retry loop simply tries the operation again. )-:  It's not
1508          * pretty but an acceptable workaround until we work out what the
1509          * real problem is. */
1510
1511         retry = 0;
1512         do {
1513
1514                 DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
1515                         domain->name ));
1516
1517                 status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
1518                 if (!NT_STATUS_IS_OK(status)) {
1519                         DEBUG(3, ("query_user_list: returned 0x%08x, "
1520                                   "retrying\n", NT_STATUS_V(status)));
1521                 }
1522                 if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
1523                         DEBUG(3, ("query_user_list: flushing "
1524                                   "connection cache\n"));
1525                         invalidate_cm_connection(domain);
1526                 }
1527                 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1528                     NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1529                         if (!domain->internal && old_status) {
1530                                 set_domain_offline(domain);
1531                         }
1532                         /* store partial response. */
1533                         if (*num_entries > 0) {
1534                                 /*
1535                                  * humm, what about the status used for cache?
1536                                  * Should it be NT_STATUS_OK?
1537                                  */
1538                                 break;
1539                         }
1540                         /*
1541                          * domain is offline now, and there is no user entries,
1542                          * try to fetch from cache again.
1543                          */
1544                         if (cache->tdb && !domain->online && !domain->internal && old_status) {
1545                                 centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
1546                                 /* partial response... */
1547                                 if (!centry) {
1548                                         goto skip_save;
1549                                 } else {
1550                                         goto do_fetch_cache;
1551                                 }
1552                         } else {
1553                                 goto skip_save;
1554                         }
1555                 }
1556
1557         } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && 
1558                  (retry++ < 5));
1559
1560         /* and save it */
1561         refresh_sequence_number(domain, false);
1562         if (!NT_STATUS_IS_OK(status)) {
1563                 return status;
1564         }
1565         centry = centry_start(domain, status);
1566         if (!centry)
1567                 goto skip_save;
1568         centry_put_uint32(centry, *num_entries);
1569         for (i=0; i<(*num_entries); i++) {
1570                 centry_put_string(centry, (*info)[i].acct_name);
1571                 centry_put_string(centry, (*info)[i].full_name);
1572                 centry_put_string(centry, (*info)[i].homedir);
1573                 centry_put_string(centry, (*info)[i].shell);
1574                 centry_put_sid(centry, &(*info)[i].user_sid);
1575                 centry_put_sid(centry, &(*info)[i].group_sid);
1576                 if (domain->backend && domain->backend->consistent) {
1577                         /* when the backend is consistent we can pre-prime some mappings */
1578                         wcache_save_name_to_sid(domain, NT_STATUS_OK, 
1579                                                 domain->name,
1580                                                 (*info)[i].acct_name, 
1581                                                 &(*info)[i].user_sid,
1582                                                 SID_NAME_USER);
1583                         wcache_save_sid_to_name(domain, NT_STATUS_OK, 
1584                                                 &(*info)[i].user_sid,
1585                                                 domain->name,
1586                                                 (*info)[i].acct_name, 
1587                                                 SID_NAME_USER);
1588                         wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
1589                 }
1590         }       
1591         centry_end(centry, "UL/%s", domain->name);
1592         centry_free(centry);
1593
1594 skip_save:
1595         return status;
1596 }
1597
1598 /* list all domain groups */
1599 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
1600                                 TALLOC_CTX *mem_ctx,
1601                                 uint32_t *num_entries,
1602                                 struct wb_acct_info **info)
1603 {
1604         struct winbind_cache *cache = get_cache(domain);
1605         struct cache_entry *centry = NULL;
1606         NTSTATUS status;
1607         unsigned int i;
1608         bool old_status;
1609
1610         old_status = domain->online;
1611         if (!cache->tdb)
1612                 goto do_query;
1613
1614         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1615         if (!centry)
1616                 goto do_query;
1617
1618 do_fetch_cache:
1619         *num_entries = centry_uint32(centry);
1620
1621         if (*num_entries == 0)
1622                 goto do_cached;
1623
1624         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1625         if (! (*info)) {
1626                 smb_panic_fn("enum_dom_groups out of memory");
1627         }
1628         for (i=0; i<(*num_entries); i++) {
1629                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1630                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1631                 (*info)[i].rid = centry_uint32(centry);
1632         }
1633
1634 do_cached:      
1635         status = centry->status;
1636
1637         DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
1638                 domain->name, nt_errstr(status) ));
1639
1640         centry_free(centry);
1641         return status;
1642
1643 do_query:
1644         *num_entries = 0;
1645         *info = NULL;
1646
1647         /* Return status value returned by seq number check */
1648
1649         if (!NT_STATUS_IS_OK(domain->last_status))
1650                 return domain->last_status;
1651
1652         DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
1653                 domain->name ));
1654
1655         status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
1656
1657         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1658             NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1659                 if (!domain->internal && old_status) {
1660                         set_domain_offline(domain);
1661                 }
1662                 if (cache->tdb &&
1663                         !domain->online &&
1664                         !domain->internal &&
1665                         old_status) {
1666                         centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
1667                         if (centry) {
1668                                 goto do_fetch_cache;
1669                         }
1670                 }
1671         }
1672         /* and save it */
1673         refresh_sequence_number(domain, false);
1674         if (!NT_STATUS_IS_OK(status)) {
1675                 return status;
1676         }
1677         centry = centry_start(domain, status);
1678         if (!centry)
1679                 goto skip_save;
1680         centry_put_uint32(centry, *num_entries);
1681         for (i=0; i<(*num_entries); i++) {
1682                 centry_put_string(centry, (*info)[i].acct_name);
1683                 centry_put_string(centry, (*info)[i].acct_desc);
1684                 centry_put_uint32(centry, (*info)[i].rid);
1685         }       
1686         centry_end(centry, "GL/%s/domain", domain->name);
1687         centry_free(centry);
1688
1689 skip_save:
1690         return status;
1691 }
1692
1693 /* list all domain groups */
1694 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
1695                                 TALLOC_CTX *mem_ctx,
1696                                 uint32_t *num_entries,
1697                                 struct wb_acct_info **info)
1698 {
1699         struct winbind_cache *cache = get_cache(domain);
1700         struct cache_entry *centry = NULL;
1701         NTSTATUS status;
1702         unsigned int i;
1703         bool old_status;
1704
1705         old_status = domain->online;
1706         if (!cache->tdb)
1707                 goto do_query;
1708
1709         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1710         if (!centry)
1711                 goto do_query;
1712
1713 do_fetch_cache:
1714         *num_entries = centry_uint32(centry);
1715
1716         if (*num_entries == 0)
1717                 goto do_cached;
1718
1719         (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
1720         if (! (*info)) {
1721                 smb_panic_fn("enum_dom_groups out of memory");
1722         }
1723         for (i=0; i<(*num_entries); i++) {
1724                 fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
1725                 fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
1726                 (*info)[i].rid = centry_uint32(centry);
1727         }
1728
1729 do_cached:      
1730
1731         /* If we are returning cached data and the domain controller
1732            is down then we don't know whether the data is up to date
1733            or not.  Return NT_STATUS_MORE_PROCESSING_REQUIRED to
1734            indicate this. */
1735
1736         if (wcache_server_down(domain)) {
1737                 DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
1738                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
1739         } else
1740                 status = centry->status;
1741
1742         DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
1743                 domain->name, nt_errstr(status) ));
1744
1745         centry_free(centry);
1746         return status;
1747
1748 do_query:
1749         *num_entries = 0;
1750         *info = NULL;
1751
1752         /* Return status value returned by seq number check */
1753
1754         if (!NT_STATUS_IS_OK(domain->last_status))
1755                 return domain->last_status;
1756
1757         DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
1758                 domain->name ));
1759
1760         status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
1761
1762         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1763                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1764                 if (!domain->internal && old_status) {
1765                         set_domain_offline(domain);
1766                 }
1767                 if (cache->tdb &&
1768                         !domain->internal &&
1769                         !domain->online &&
1770                         old_status) {
1771                         centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
1772                         if (centry) {
1773                                 goto do_fetch_cache;
1774                         }
1775                 }
1776         }
1777         /* and save it */
1778         refresh_sequence_number(domain, false);
1779         if (!NT_STATUS_IS_OK(status)) {
1780                 return status;
1781         }
1782         centry = centry_start(domain, status);
1783         if (!centry)
1784                 goto skip_save;
1785         centry_put_uint32(centry, *num_entries);
1786         for (i=0; i<(*num_entries); i++) {
1787                 centry_put_string(centry, (*info)[i].acct_name);
1788                 centry_put_string(centry, (*info)[i].acct_desc);
1789                 centry_put_uint32(centry, (*info)[i].rid);
1790         }
1791         centry_end(centry, "GL/%s/local", domain->name);
1792         centry_free(centry);
1793
1794 skip_save:
1795         return status;
1796 }
1797
1798 NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
1799                             const char *domain_name,
1800                             const char *name,
1801                             struct dom_sid *sid,
1802                             enum lsa_SidType *type)
1803 {
1804         struct winbind_cache *cache = get_cache(domain);
1805         struct cache_entry *centry;
1806         NTSTATUS status;
1807         char *uname;
1808
1809         if (cache->tdb == NULL) {
1810                 return NT_STATUS_NOT_FOUND;
1811         }
1812
1813         uname = talloc_strdup_upper(talloc_tos(), name);
1814         if (uname == NULL) {
1815                 return NT_STATUS_NO_MEMORY;
1816         }
1817
1818         if ((domain_name == NULL) || (domain_name[0] == '\0')) {
1819                 domain_name = domain->name;
1820         }
1821
1822         centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
1823         TALLOC_FREE(uname);
1824         if (centry == NULL) {
1825                 return NT_STATUS_NOT_FOUND;
1826         }
1827
1828         status = centry->status;
1829         if (NT_STATUS_IS_OK(status)) {
1830                 *type = (enum lsa_SidType)centry_uint32(centry);
1831                 centry_sid(centry, sid);
1832         }
1833
1834         DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: "
1835                   "%s\n", domain->name, nt_errstr(status) ));
1836
1837         centry_free(centry);
1838         return status;
1839 }
1840
1841 /* convert a single name to a sid in a domain */
1842 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
1843                             TALLOC_CTX *mem_ctx,
1844                             const char *domain_name,
1845                             const char *name,
1846                             uint32_t flags,
1847                             struct dom_sid *sid,
1848                             enum lsa_SidType *type)
1849 {
1850         NTSTATUS status;
1851         bool old_status;
1852
1853         old_status = domain->online;
1854
1855         status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1856         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1857                 return status;
1858         }
1859
1860         ZERO_STRUCTP(sid);
1861
1862         /* If the seq number check indicated that there is a problem
1863          * with this DC, then return that status... except for
1864          * access_denied.  This is special because the dc may be in
1865          * "restrict anonymous = 1" mode, in which case it will deny
1866          * most unauthenticated operations, but *will* allow the LSA
1867          * name-to-sid that we try as a fallback. */
1868
1869         if (!(NT_STATUS_IS_OK(domain->last_status)
1870               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1871                 return domain->last_status;
1872
1873         DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
1874                 domain->name ));
1875
1876         status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
1877                                               name, flags, sid, type);
1878
1879         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1880                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1881                 if (!domain->internal && old_status) {
1882                         set_domain_offline(domain);
1883                 }
1884                 if (!domain->internal &&
1885                         !domain->online &&
1886                         old_status) {
1887                         NTSTATUS cache_status;
1888                         cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
1889                         return cache_status;
1890                 }
1891         }
1892         /* and save it */
1893         refresh_sequence_number(domain, false);
1894
1895         if (domain->online &&
1896             (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
1897                 wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
1898
1899                 /* Only save the reverse mapping if this was not a UPN */
1900                 if (!strchr(name, '@')) {
1901                         if (!strupper_m(discard_const_p(char, domain_name))) {
1902                                 return NT_STATUS_INVALID_PARAMETER;
1903                         }
1904                         (void)strlower_m(discard_const_p(char, name));
1905                         wcache_save_sid_to_name(domain, status, sid, domain_name, name, *type);
1906                 }
1907         }
1908
1909         return status;
1910 }
1911
1912 static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
1913                                    const struct dom_sid *sid,
1914                                    TALLOC_CTX *mem_ctx,
1915                                    char **domain_name,
1916                                    char **name,
1917                                    enum lsa_SidType *type)
1918 {
1919         struct winbind_cache *cache = get_cache(domain);
1920         struct cache_entry *centry;
1921         char *sid_string;
1922         NTSTATUS status;
1923
1924         if (cache->tdb == NULL) {
1925                 return NT_STATUS_NOT_FOUND;
1926         }
1927
1928         sid_string = sid_string_tos(sid);
1929         if (sid_string == NULL) {
1930                 return NT_STATUS_NO_MEMORY;
1931         }
1932
1933         centry = wcache_fetch(cache, domain, "SN/%s", sid_string);
1934         TALLOC_FREE(sid_string);
1935         if (centry == NULL) {
1936                 return NT_STATUS_NOT_FOUND;
1937         }
1938
1939         if (NT_STATUS_IS_OK(centry->status)) {
1940                 *type = (enum lsa_SidType)centry_uint32(centry);
1941                 *domain_name = centry_string(centry, mem_ctx);
1942                 *name = centry_string(centry, mem_ctx);
1943         }
1944
1945         status = centry->status;
1946         centry_free(centry);
1947
1948         DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: "
1949                   "%s\n", domain->name, nt_errstr(status) ));
1950
1951         return status;
1952 }
1953
1954 /* convert a sid to a user or group name. The sid is guaranteed to be in the domain
1955    given */
1956 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
1957                             TALLOC_CTX *mem_ctx,
1958                             const struct dom_sid *sid,
1959                             char **domain_name,
1960                             char **name,
1961                             enum lsa_SidType *type)
1962 {
1963         NTSTATUS status;
1964         bool old_status;
1965
1966         old_status = domain->online;
1967         status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
1968                                     type);
1969         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
1970                 return status;
1971         }
1972
1973         *name = NULL;
1974         *domain_name = NULL;
1975
1976         /* If the seq number check indicated that there is a problem
1977          * with this DC, then return that status... except for
1978          * access_denied.  This is special because the dc may be in
1979          * "restrict anonymous = 1" mode, in which case it will deny
1980          * most unauthenticated operations, but *will* allow the LSA
1981          * sid-to-name that we try as a fallback. */
1982
1983         if (!(NT_STATUS_IS_OK(domain->last_status)
1984               || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
1985                 return domain->last_status;
1986
1987         DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
1988                 domain->name ));
1989
1990         status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
1991
1992         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
1993                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1994                 if (!domain->internal && old_status) {
1995                         set_domain_offline(domain);
1996                 }
1997                 if (!domain->internal &&
1998                         !domain->online &&
1999                         old_status) {
2000                         NTSTATUS cache_status;
2001                         cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
2002                                                         domain_name, name, type);
2003                         return cache_status;
2004                 }
2005         }
2006         /* and save it */
2007         refresh_sequence_number(domain, false);
2008         if (!NT_STATUS_IS_OK(status)) {
2009                 return status;
2010         }
2011         wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
2012
2013         /* We can't save the name to sid mapping here, as with sid history a
2014          * later name2sid would give the wrong sid. */
2015
2016         return status;
2017 }
2018
2019 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
2020                               TALLOC_CTX *mem_ctx,
2021                               const struct dom_sid *domain_sid,
2022                               uint32_t *rids,
2023                               size_t num_rids,
2024                               char **domain_name,
2025                               char ***names,
2026                               enum lsa_SidType **types)
2027 {
2028         struct winbind_cache *cache = get_cache(domain);
2029         size_t i;
2030         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
2031         bool have_mapped;
2032         bool have_unmapped;
2033         bool old_status;
2034
2035         old_status = domain->online;
2036         *domain_name = NULL;
2037         *names = NULL;
2038         *types = NULL;
2039
2040         if (!cache->tdb) {
2041                 goto do_query;
2042         }
2043
2044         if (num_rids == 0) {
2045                 return NT_STATUS_OK;
2046         }
2047
2048         *names = talloc_array(mem_ctx, char *, num_rids);
2049         *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
2050
2051         if ((*names == NULL) || (*types == NULL)) {
2052                 result = NT_STATUS_NO_MEMORY;
2053                 goto error;
2054         }
2055
2056         have_mapped = have_unmapped = false;
2057
2058         for (i=0; i<num_rids; i++) {
2059                 struct dom_sid sid;
2060                 struct cache_entry *centry;
2061                 fstring tmp;
2062
2063                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2064                         result = NT_STATUS_INTERNAL_ERROR;
2065                         goto error;
2066                 }
2067
2068                 centry = wcache_fetch(cache, domain, "SN/%s",
2069                                       sid_to_fstring(tmp, &sid));
2070                 if (!centry) {
2071                         goto do_query;
2072                 }
2073
2074                 (*types)[i] = SID_NAME_UNKNOWN;
2075                 (*names)[i] = talloc_strdup(*names, "");
2076
2077                 if (NT_STATUS_IS_OK(centry->status)) {
2078                         char *dom;
2079                         have_mapped = true;
2080                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2081
2082                         dom = centry_string(centry, mem_ctx);
2083                         if (*domain_name == NULL) {
2084                                 *domain_name = dom;
2085                         } else {
2086                                 talloc_free(dom);
2087                         }
2088
2089                         (*names)[i] = centry_string(centry, *names);
2090
2091                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)
2092                            || NT_STATUS_EQUAL(centry->status, STATUS_SOME_UNMAPPED)) {
2093                         have_unmapped = true;
2094
2095                 } else {
2096                         /* something's definitely wrong */
2097                         result = centry->status;
2098                         centry_free(centry);
2099                         goto error;
2100                 }
2101
2102                 centry_free(centry);
2103         }
2104
2105         if (!have_mapped) {
2106                 return NT_STATUS_NONE_MAPPED;
2107         }
2108         if (!have_unmapped) {
2109                 return NT_STATUS_OK;
2110         }
2111         return STATUS_SOME_UNMAPPED;
2112
2113  do_query:
2114
2115         TALLOC_FREE(*names);
2116         TALLOC_FREE(*types);
2117
2118         result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
2119                                                 rids, num_rids, domain_name,
2120                                                 names, types);
2121
2122         if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
2123                 NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2124                 if (!domain->internal && old_status) {
2125                         set_domain_offline(domain);
2126                 }
2127                 if (cache->tdb &&
2128                         !domain->internal &&
2129                         !domain->online &&
2130                         old_status) {
2131                         have_mapped = have_unmapped = false;
2132
2133                         *names = talloc_array(mem_ctx, char *, num_rids);
2134                         if (*names == NULL) {
2135                                 result = NT_STATUS_NO_MEMORY;
2136                                 goto error;
2137                         }
2138
2139                         *types = talloc_array(mem_ctx, enum lsa_SidType,
2140                                               num_rids);
2141                         if (*types == NULL) {
2142                                 result = NT_STATUS_NO_MEMORY;
2143                                 goto error;
2144                         }
2145
2146                         for (i=0; i<num_rids; i++) {
2147                                 struct dom_sid sid;
2148                                 struct cache_entry *centry;
2149                                 fstring tmp;
2150
2151                                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2152                                         result = NT_STATUS_INTERNAL_ERROR;
2153                                         goto error;
2154                                 }
2155
2156                                 centry = wcache_fetch(cache, domain, "SN/%s",
2157                                                       sid_to_fstring(tmp, &sid));
2158                                 if (!centry) {
2159                                         (*types)[i] = SID_NAME_UNKNOWN;
2160                                         (*names)[i] = talloc_strdup(*names, "");
2161                                         continue;
2162                                 }
2163
2164                                 (*types)[i] = SID_NAME_UNKNOWN;
2165                                 (*names)[i] = talloc_strdup(*names, "");
2166
2167                                 if (NT_STATUS_IS_OK(centry->status)) {
2168                                         char *dom;
2169                                         have_mapped = true;
2170                                         (*types)[i] = (enum lsa_SidType)centry_uint32(centry);
2171
2172                                         dom = centry_string(centry, mem_ctx);
2173                                         if (*domain_name == NULL) {
2174                                                 *domain_name = dom;
2175                                         } else {
2176                                                 talloc_free(dom);
2177                                         }
2178
2179                                         (*names)[i] = centry_string(centry, *names);
2180
2181                                 } else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
2182                                         have_unmapped = true;
2183
2184                                 } else {
2185                                         /* something's definitely wrong */
2186                                         result = centry->status;
2187                                         centry_free(centry);
2188                                         goto error;
2189                                 }
2190
2191                                 centry_free(centry);
2192                         }
2193
2194                         if (!have_mapped) {
2195                                 return NT_STATUS_NONE_MAPPED;
2196                         }
2197                         if (!have_unmapped) {
2198                                 return NT_STATUS_OK;
2199                         }
2200                         return STATUS_SOME_UNMAPPED;
2201                 }
2202         }
2203         /*
2204           None of the queried rids has been found so save all negative entries
2205         */
2206         if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
2207                 for (i = 0; i < num_rids; i++) {
2208                         struct dom_sid sid;
2209                         const char *name = "";
2210                         const enum lsa_SidType type = SID_NAME_UNKNOWN;
2211                         NTSTATUS status = NT_STATUS_NONE_MAPPED;
2212
2213                         if (!sid_compose(&sid, domain_sid, rids[i])) {
2214                                 return NT_STATUS_INTERNAL_ERROR;
2215                         }
2216
2217                         wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2218                                                 name, type);
2219                 }
2220
2221                 return result;
2222         }
2223
2224         /*
2225           Some or all of the queried rids have been found.
2226         */
2227         if (!NT_STATUS_IS_OK(result) &&
2228             !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
2229                 return result;
2230         }
2231
2232         refresh_sequence_number(domain, false);
2233
2234         for (i=0; i<num_rids; i++) {
2235                 struct dom_sid sid;
2236                 NTSTATUS status;
2237
2238                 if (!sid_compose(&sid, domain_sid, rids[i])) {
2239                         result = NT_STATUS_INTERNAL_ERROR;
2240                         goto error;
2241                 }
2242
2243                 status = (*types)[i] == SID_NAME_UNKNOWN ?
2244                         NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
2245
2246                 wcache_save_sid_to_name(domain, status, &sid, *domain_name,
2247                                         (*names)[i], (*types)[i]);
2248         }
2249
2250         return result;
2251
2252  error:
2253         TALLOC_FREE(*names);
2254         TALLOC_FREE(*types);
2255         return result;
2256 }
2257
2258 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
2259                            TALLOC_CTX *mem_ctx,
2260                            const struct dom_sid *user_sid,
2261                            struct wbint_userinfo *info)
2262 {
2263         struct winbind_cache *cache = get_cache(domain);
2264         struct cache_entry *centry = NULL;
2265         NTSTATUS status;
2266         char *sid_string;
2267
2268         if (cache->tdb == NULL) {
2269                 return NT_STATUS_NOT_FOUND;
2270         }
2271
2272         sid_string = sid_string_tos(user_sid);
2273         if (sid_string == NULL) {
2274                 return NT_STATUS_NO_MEMORY;
2275         }
2276
2277         centry = wcache_fetch(cache, domain, "U/%s", sid_string);
2278         TALLOC_FREE(sid_string);
2279         if (centry == NULL) {
2280                 return NT_STATUS_NOT_FOUND;
2281         }
2282
2283         /*
2284          * If we have an access denied cache entry and a cached info3
2285          * in the samlogon cache then do a query.  This will force the
2286          * rpc back end to return the info3 data.
2287          */
2288
2289         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED) &&
2290             netsamlogon_cache_have(user_sid)) {
2291                 DEBUG(10, ("query_user: cached access denied and have cached "
2292                            "info3\n"));
2293                 domain->last_status = NT_STATUS_OK;
2294                 centry_free(centry);
2295                 return NT_STATUS_NOT_FOUND;
2296         }
2297
2298         /* if status is not ok then this is a negative hit
2299            and the rest of the data doesn't matter */
2300         status = centry->status;
2301         if (NT_STATUS_IS_OK(status)) {
2302                 info->acct_name = centry_string(centry, mem_ctx);
2303                 info->full_name = centry_string(centry, mem_ctx);
2304                 info->homedir = centry_string(centry, mem_ctx);
2305                 info->shell = centry_string(centry, mem_ctx);
2306                 info->primary_gid = centry_uint32(centry);
2307                 centry_sid(centry, &info->user_sid);
2308                 centry_sid(centry, &info->group_sid);
2309         }
2310
2311         DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
2312                   "%s\n", domain->name, nt_errstr(status) ));
2313
2314         centry_free(centry);
2315         return status;
2316 }
2317
2318
2319 /**
2320 * @brief Query a fullname from the username cache (for further gecos processing)
2321 *
2322 * @param domain         A pointer to the winbindd_domain struct.
2323 * @param mem_ctx        The talloc context.
2324 * @param user_sid       The user sid.
2325 * @param full_name      A pointer to the full_name string.
2326 *
2327 * @return NTSTATUS code
2328 */
2329 NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
2330                                     TALLOC_CTX *mem_ctx,
2331                                     const struct dom_sid *user_sid,
2332                                     const char **full_name)
2333 {
2334         NTSTATUS status;
2335         struct wbint_userinfo info;
2336
2337         status = wcache_query_user(domain, mem_ctx, user_sid, &info);
2338         if (!NT_STATUS_IS_OK(status)) {
2339                 return status;
2340         }
2341
2342         if (info.full_name != NULL) {
2343                 *full_name = talloc_strdup(mem_ctx, info.full_name);
2344                 if (*full_name == NULL) {
2345                         return NT_STATUS_NO_MEMORY;
2346                 }
2347         }
2348
2349         return NT_STATUS_OK;
2350 }
2351
2352 /* Lookup user information from a rid */
2353 static NTSTATUS query_user(struct winbindd_domain *domain,
2354                            TALLOC_CTX *mem_ctx,
2355                            const struct dom_sid *user_sid,
2356                            struct wbint_userinfo *info)
2357 {
2358         NTSTATUS status;
2359         bool old_status;
2360
2361         old_status = domain->online;
2362         status = wcache_query_user(domain, mem_ctx, user_sid, info);
2363         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2364                 return status;
2365         }
2366
2367         ZERO_STRUCTP(info);
2368
2369         /* Return status value returned by seq number check */
2370
2371         if (!NT_STATUS_IS_OK(domain->last_status))
2372                 return domain->last_status;
2373
2374         DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
2375                 domain->name ));
2376
2377         status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
2378
2379         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2380                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2381                 if (!domain->internal && old_status) {
2382                         set_domain_offline(domain);
2383                 }
2384                 if (!domain->internal &&
2385                         !domain->online &&
2386                         old_status) {
2387                         NTSTATUS cache_status;
2388                         cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
2389                         return cache_status;
2390                 }
2391         }
2392         /* and save it */
2393         refresh_sequence_number(domain, false);
2394         if (!NT_STATUS_IS_OK(status)) {
2395                 return status;
2396         }
2397         wcache_save_user(domain, status, info);
2398
2399         return status;
2400 }
2401
2402 NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
2403                                   TALLOC_CTX *mem_ctx,
2404                                   const struct dom_sid *user_sid,
2405                                   uint32_t *pnum_sids,
2406                                   struct dom_sid **psids)
2407 {
2408         struct winbind_cache *cache = get_cache(domain);
2409         struct cache_entry *centry = NULL;
2410         NTSTATUS status;
2411         uint32_t i, num_sids;
2412         struct dom_sid *sids;
2413         fstring sid_string;
2414
2415         if (cache->tdb == NULL) {
2416                 return NT_STATUS_NOT_FOUND;
2417         }
2418
2419         centry = wcache_fetch(cache, domain, "UG/%s",
2420                               sid_to_fstring(sid_string, user_sid));
2421         if (centry == NULL) {
2422                 return NT_STATUS_NOT_FOUND;
2423         }
2424
2425         /* If we have an access denied cache entry and a cached info3 in the
2426            samlogon cache then do a query.  This will force the rpc back end
2427            to return the info3 data. */
2428
2429         if (NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)
2430             && netsamlogon_cache_have(user_sid)) {
2431                 DEBUG(10, ("lookup_usergroups: cached access denied and have "
2432                            "cached info3\n"));
2433                 domain->last_status = NT_STATUS_OK;
2434                 centry_free(centry);
2435                 return NT_STATUS_NOT_FOUND;
2436         }
2437
2438         num_sids = centry_uint32(centry);
2439         sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
2440         if (sids == NULL) {
2441                 centry_free(centry);
2442                 return NT_STATUS_NO_MEMORY;
2443         }
2444
2445         for (i=0; i<num_sids; i++) {
2446                 centry_sid(centry, &sids[i]);
2447         }
2448
2449         status = centry->status;
2450
2451         DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s "
2452                   "status: %s\n", domain->name, nt_errstr(status)));
2453
2454         centry_free(centry);
2455
2456         *pnum_sids = num_sids;
2457         *psids = sids;
2458         return status;
2459 }
2460
2461 /* Lookup groups a user is a member of. */
2462 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
2463                                   TALLOC_CTX *mem_ctx,
2464                                   const struct dom_sid *user_sid,
2465                                   uint32_t *num_groups, struct dom_sid **user_gids)
2466 {
2467         struct cache_entry *centry = NULL;
2468         NTSTATUS status;
2469         unsigned int i;
2470         fstring sid_string;
2471         bool old_status;
2472
2473         old_status = domain->online;
2474         status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2475                                           num_groups, user_gids);
2476         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2477                 return status;
2478         }
2479
2480         (*num_groups) = 0;
2481         (*user_gids) = NULL;
2482
2483         /* Return status value returned by seq number check */
2484
2485         if (!NT_STATUS_IS_OK(domain->last_status))
2486                 return domain->last_status;
2487
2488         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
2489                 domain->name ));
2490
2491         status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
2492
2493         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2494                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2495                 if (!domain->internal && old_status) {
2496                         set_domain_offline(domain);
2497                 }
2498                 if (!domain->internal &&
2499                         !domain->online &&
2500                         old_status) {
2501                         NTSTATUS cache_status;
2502                         cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
2503                                                           num_groups, user_gids);
2504                         return cache_status;
2505                 }
2506         }
2507         if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
2508                 goto skip_save;
2509
2510         /* and save it */
2511         refresh_sequence_number(domain, false);
2512         if (!NT_STATUS_IS_OK(status)) {
2513                 return status;
2514         }
2515         centry = centry_start(domain, status);
2516         if (!centry)
2517                 goto skip_save;
2518
2519         centry_put_uint32(centry, *num_groups);
2520         for (i=0; i<(*num_groups); i++) {
2521                 centry_put_sid(centry, &(*user_gids)[i]);
2522         }       
2523
2524         centry_end(centry, "UG/%s", sid_to_fstring(sid_string, user_sid));
2525         centry_free(centry);
2526
2527 skip_save:
2528         return status;
2529 }
2530
2531 static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
2532                                  const struct dom_sid *sids)
2533 {
2534         uint32_t i;
2535         char *sidlist;
2536
2537         sidlist = talloc_strdup(mem_ctx, "");
2538         if (sidlist == NULL) {
2539                 return NULL;
2540         }
2541         for (i=0; i<num_sids; i++) {
2542                 fstring tmp;
2543                 sidlist = talloc_asprintf_append_buffer(
2544                         sidlist, "/%s", sid_to_fstring(tmp, &sids[i]));
2545                 if (sidlist == NULL) {
2546                         return NULL;
2547                 }
2548         }
2549         return sidlist;
2550 }
2551
2552 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
2553                                    TALLOC_CTX *mem_ctx, uint32_t num_sids,
2554                                    const struct dom_sid *sids,
2555                                    uint32_t *pnum_aliases, uint32_t **paliases)
2556 {
2557         struct winbind_cache *cache = get_cache(domain);
2558         struct cache_entry *centry = NULL;
2559         uint32_t num_aliases;
2560         uint32_t *aliases;
2561         NTSTATUS status;
2562         char *sidlist;
2563         int i;
2564
2565         if (cache->tdb == NULL) {
2566                 return NT_STATUS_NOT_FOUND;
2567         }
2568
2569         if (num_sids == 0) {
2570                 *pnum_aliases = 0;
2571                 *paliases = NULL;
2572                 return NT_STATUS_OK;
2573         }
2574
2575         /* We need to cache indexed by the whole list of SIDs, the aliases
2576          * resulting might come from any of the SIDs. */
2577
2578         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2579         if (sidlist == NULL) {
2580                 return NT_STATUS_NO_MEMORY;
2581         }
2582
2583         centry = wcache_fetch(cache, domain, "UA%s", sidlist);
2584         TALLOC_FREE(sidlist);
2585         if (centry == NULL) {
2586                 return NT_STATUS_NOT_FOUND;
2587         }
2588
2589         num_aliases = centry_uint32(centry);
2590         aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
2591         if (aliases == NULL) {
2592                 centry_free(centry);
2593                 return NT_STATUS_NO_MEMORY;
2594         }
2595
2596         for (i=0; i<num_aliases; i++) {
2597                 aliases[i] = centry_uint32(centry);
2598         }
2599
2600         status = centry->status;
2601
2602         DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
2603                   "status %s\n", domain->name, nt_errstr(status)));
2604
2605         centry_free(centry);
2606
2607         *pnum_aliases = num_aliases;
2608         *paliases = aliases;
2609
2610         return status;
2611 }
2612
2613 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
2614                                    TALLOC_CTX *mem_ctx,
2615                                    uint32_t num_sids, const struct dom_sid *sids,
2616                                    uint32_t *num_aliases, uint32_t **alias_rids)
2617 {
2618         struct cache_entry *centry = NULL;
2619         NTSTATUS status;
2620         char *sidlist;
2621         int i;
2622         bool old_status;
2623
2624         old_status = domain->online;
2625         status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
2626                                            num_aliases, alias_rids);
2627         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2628                 return status;
2629         }
2630
2631         (*num_aliases) = 0;
2632         (*alias_rids) = NULL;
2633
2634         if (!NT_STATUS_IS_OK(domain->last_status))
2635                 return domain->last_status;
2636
2637         DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info "
2638                   "for domain %s\n", domain->name ));
2639
2640         sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
2641         if (sidlist == NULL) {
2642                 return NT_STATUS_NO_MEMORY;
2643         }
2644
2645         status = domain->backend->lookup_useraliases(domain, mem_ctx,
2646                                                      num_sids, sids,
2647                                                      num_aliases, alias_rids);
2648
2649         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2650                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2651                 if (!domain->internal && old_status) {
2652                         set_domain_offline(domain);
2653                 }
2654                 if (!domain->internal &&
2655                         !domain->online &&
2656                         old_status) {
2657                         NTSTATUS cache_status;
2658                         cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
2659                                                                  sids, num_aliases, alias_rids);
2660                         return cache_status;
2661                 }
2662         }
2663         /* and save it */
2664         refresh_sequence_number(domain, false);
2665         if (!NT_STATUS_IS_OK(status)) {
2666                 return status;
2667         }
2668         centry = centry_start(domain, status);
2669         if (!centry)
2670                 goto skip_save;
2671         centry_put_uint32(centry, *num_aliases);
2672         for (i=0; i<(*num_aliases); i++)
2673                 centry_put_uint32(centry, (*alias_rids)[i]);
2674         centry_end(centry, "UA%s", sidlist);
2675         centry_free(centry);
2676
2677  skip_save:
2678         return status;
2679 }
2680
2681 NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
2682                                 TALLOC_CTX *mem_ctx,
2683                                 const struct dom_sid *group_sid,
2684                                 uint32_t *num_names,
2685                                 struct dom_sid **sid_mem, char ***names,
2686                                 uint32_t **name_types)
2687 {
2688         struct winbind_cache *cache = get_cache(domain);
2689         struct cache_entry *centry = NULL;
2690         NTSTATUS status;
2691         unsigned int i;
2692         char *sid_string;
2693
2694         if (cache->tdb == NULL) {
2695                 return NT_STATUS_NOT_FOUND;
2696         }
2697
2698         sid_string = sid_string_tos(group_sid);
2699         if (sid_string == NULL) {
2700                 return NT_STATUS_NO_MEMORY;
2701         }
2702
2703         centry = wcache_fetch(cache, domain, "GM/%s", sid_string);
2704         TALLOC_FREE(sid_string);
2705         if (centry == NULL) {
2706                 return NT_STATUS_NOT_FOUND;
2707         }
2708
2709         *sid_mem = NULL;
2710         *names = NULL;
2711         *name_types = NULL;
2712
2713         *num_names = centry_uint32(centry);
2714         if (*num_names == 0) {
2715                 centry_free(centry);
2716                 return NT_STATUS_OK;
2717         }
2718
2719         *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
2720         *names = talloc_array(mem_ctx, char *, *num_names);
2721         *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
2722
2723         if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
2724                 TALLOC_FREE(*sid_mem);
2725                 TALLOC_FREE(*names);
2726                 TALLOC_FREE(*name_types);
2727                 centry_free(centry);
2728                 return NT_STATUS_NO_MEMORY;
2729         }
2730
2731         for (i=0; i<(*num_names); i++) {
2732                 centry_sid(centry, &(*sid_mem)[i]);
2733                 (*names)[i] = centry_string(centry, mem_ctx);
2734                 (*name_types)[i] = centry_uint32(centry);
2735         }
2736
2737         status = centry->status;
2738
2739         DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s "
2740                   "status: %s\n", domain->name, nt_errstr(status)));
2741
2742         centry_free(centry);
2743         return status;
2744 }
2745
2746 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
2747                                 TALLOC_CTX *mem_ctx,
2748                                 const struct dom_sid *group_sid,
2749                                 enum lsa_SidType type,
2750                                 uint32_t *num_names,
2751                                 struct dom_sid **sid_mem, char ***names,
2752                                 uint32_t **name_types)
2753 {
2754         struct cache_entry *centry = NULL;
2755         NTSTATUS status;
2756         unsigned int i;
2757         fstring sid_string;
2758         bool old_status;
2759
2760         old_status = domain->online;
2761         status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
2762                                         sid_mem, names, name_types);
2763         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
2764                 return status;
2765         }
2766
2767         (*num_names) = 0;
2768         (*sid_mem) = NULL;
2769         (*names) = NULL;
2770         (*name_types) = NULL;
2771
2772         /* Return status value returned by seq number check */
2773
2774         if (!NT_STATUS_IS_OK(domain->last_status))
2775                 return domain->last_status;
2776
2777         DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
2778                 domain->name ));
2779
2780         status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
2781                                                   type, num_names,
2782                                                   sid_mem, names, name_types);
2783
2784         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2785                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2786                 if (!domain->internal && old_status) {
2787                         set_domain_offline(domain);
2788                 }
2789                 if (!domain->internal &&
2790                         !domain->online &&
2791                         old_status) {
2792                         NTSTATUS cache_status;
2793                         cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
2794                                                               num_names, sid_mem, names,
2795                                                               name_types);
2796                         return cache_status;
2797                 }
2798         }
2799         /* and save it */
2800         refresh_sequence_number(domain, false);
2801         if (!NT_STATUS_IS_OK(status)) {
2802                 return status;
2803         }
2804         centry = centry_start(domain, status);
2805         if (!centry)
2806                 goto skip_save;
2807         centry_put_uint32(centry, *num_names);
2808         for (i=0; i<(*num_names); i++) {
2809                 centry_put_sid(centry, &(*sid_mem)[i]);
2810                 centry_put_string(centry, (*names)[i]);
2811                 centry_put_uint32(centry, (*name_types)[i]);
2812         }       
2813         centry_end(centry, "GM/%s", sid_to_fstring(sid_string, group_sid));
2814         centry_free(centry);
2815
2816 skip_save:
2817         return status;
2818 }
2819
2820 /* find the sequence number for a domain */
2821 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32_t *seq)
2822 {
2823         refresh_sequence_number(domain, false);
2824
2825         *seq = domain->sequence_number;
2826
2827         return NT_STATUS_OK;
2828 }
2829
2830 /* enumerate trusted domains 
2831  * (we need to have the list of trustdoms in the cache when we go offline) -
2832  * Guenther */
2833 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
2834                                 TALLOC_CTX *mem_ctx,
2835                                 struct netr_DomainTrustList *trusts)
2836 {
2837         NTSTATUS status;
2838         struct winbind_cache *cache;
2839         struct winbindd_tdc_domain *dom_list = NULL;
2840         size_t num_domains = 0;
2841         bool retval = false;
2842         int i;
2843         bool old_status;
2844
2845         old_status = domain->online;
2846         trusts->count = 0;
2847         trusts->array = NULL;
2848
2849         cache = get_cache(domain);
2850         if (!cache || !cache->tdb) {
2851                 goto do_query;
2852         }
2853
2854         if (domain->online) {
2855                 goto do_query;
2856         }
2857
2858         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2859         if (!retval || !num_domains || !dom_list) {
2860                 TALLOC_FREE(dom_list);
2861                 goto do_query;
2862         }
2863
2864 do_fetch_cache:
2865         trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
2866         if (!trusts->array) {
2867                 TALLOC_FREE(dom_list);
2868                 return NT_STATUS_NO_MEMORY;
2869         }
2870
2871         for (i = 0; i < num_domains; i++) {
2872                 struct netr_DomainTrust *trust;
2873                 struct dom_sid *sid;
2874                 struct winbindd_domain *dom;
2875
2876                 dom = find_domain_from_name_noinit(dom_list[i].domain_name);
2877                 if (dom && dom->internal) {
2878                         continue;
2879                 }
2880
2881                 trust = &trusts->array[trusts->count];
2882                 trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
2883                 trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
2884                 sid = talloc(trusts->array, struct dom_sid);
2885                 if (!trust->netbios_name || !trust->dns_name ||
2886                         !sid) {
2887                         TALLOC_FREE(dom_list);
2888                         TALLOC_FREE(trusts->array);
2889                         return NT_STATUS_NO_MEMORY;
2890                 }
2891
2892                 trust->trust_flags = dom_list[i].trust_flags;
2893                 trust->trust_attributes = dom_list[i].trust_attribs;
2894                 trust->trust_type = dom_list[i].trust_type;
2895                 sid_copy(sid, &dom_list[i].sid);
2896                 trust->sid = sid;
2897                 trusts->count++;
2898         }
2899
2900         TALLOC_FREE(dom_list);
2901         return NT_STATUS_OK;
2902
2903 do_query:
2904         /* Return status value returned by seq number check */
2905
2906         if (!NT_STATUS_IS_OK(domain->last_status))
2907                 return domain->last_status;
2908
2909         DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
2910                 domain->name ));
2911
2912         status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
2913
2914         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2915                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2916                 if (!domain->internal && old_status) {
2917                         set_domain_offline(domain);
2918                 }
2919                 if (!domain->internal &&
2920                         !domain->online &&
2921                         old_status) {
2922                         retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
2923                         if (retval && num_domains && dom_list) {
2924                                 TALLOC_FREE(trusts->array);
2925                                 trusts->count = 0;
2926                                 goto do_fetch_cache;
2927                         }
2928                 }
2929         }
2930         /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
2931          * so that the generic centry handling still applies correctly -
2932          * Guenther*/
2933
2934         if (!NT_STATUS_IS_ERR(status)) {
2935                 status = NT_STATUS_OK;
2936         }
2937         return status;
2938 }       
2939
2940 /* get lockout policy */
2941 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
2942                                TALLOC_CTX *mem_ctx,
2943                                struct samr_DomInfo12 *policy)
2944 {
2945         struct winbind_cache *cache = get_cache(domain);
2946         struct cache_entry *centry = NULL;
2947         NTSTATUS status;
2948         bool old_status;
2949
2950         old_status = domain->online;
2951         if (!cache->tdb)
2952                 goto do_query;
2953
2954         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2955
2956         if (!centry)
2957                 goto do_query;
2958
2959 do_fetch_cache:
2960         policy->lockout_duration = centry_nttime(centry);
2961         policy->lockout_window = centry_nttime(centry);
2962         policy->lockout_threshold = centry_uint16(centry);
2963
2964         status = centry->status;
2965
2966         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
2967                 domain->name, nt_errstr(status) ));
2968
2969         centry_free(centry);
2970         return status;
2971
2972 do_query:
2973         ZERO_STRUCTP(policy);
2974
2975         /* Return status value returned by seq number check */
2976
2977         if (!NT_STATUS_IS_OK(domain->last_status))
2978                 return domain->last_status;
2979
2980         DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
2981                 domain->name ));
2982
2983         status = domain->backend->lockout_policy(domain, mem_ctx, policy);
2984
2985         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
2986                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
2987                 if (!domain->internal && old_status) {
2988                         set_domain_offline(domain);
2989                 }
2990                 if (cache->tdb &&
2991                         !domain->internal &&
2992                         !domain->online &&
2993                         old_status) {
2994                         centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
2995                         if (centry) {
2996                                 goto do_fetch_cache;
2997                         }
2998                 }
2999         }
3000         /* and save it */
3001         refresh_sequence_number(domain, false);
3002         if (!NT_STATUS_IS_OK(status)) {
3003                 return status;
3004         }
3005         wcache_save_lockout_policy(domain, status, policy);
3006
3007         return status;
3008 }
3009
3010 /* get password policy */
3011 static NTSTATUS password_policy(struct winbindd_domain *domain,
3012                                 TALLOC_CTX *mem_ctx,
3013                                 struct samr_DomInfo1 *policy)
3014 {
3015         struct winbind_cache *cache = get_cache(domain);
3016         struct cache_entry *centry = NULL;
3017         NTSTATUS status;
3018         bool old_status;
3019
3020         old_status = domain->online;
3021         if (!cache->tdb)
3022                 goto do_query;
3023
3024         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3025
3026         if (!centry)
3027                 goto do_query;
3028
3029 do_fetch_cache:
3030         policy->min_password_length = centry_uint16(centry);
3031         policy->password_history_length = centry_uint16(centry);
3032         policy->password_properties = centry_uint32(centry);
3033         policy->max_password_age = centry_nttime(centry);
3034         policy->min_password_age = centry_nttime(centry);
3035
3036         status = centry->status;
3037
3038         DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
3039                 domain->name, nt_errstr(status) ));
3040
3041         centry_free(centry);
3042         return status;
3043
3044 do_query:
3045         ZERO_STRUCTP(policy);
3046
3047         /* Return status value returned by seq number check */
3048
3049         if (!NT_STATUS_IS_OK(domain->last_status))
3050                 return domain->last_status;
3051
3052         DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
3053                 domain->name ));
3054
3055         status = domain->backend->password_policy(domain, mem_ctx, policy);
3056
3057         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
3058                 NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
3059                 if (!domain->internal && old_status) {
3060                         set_domain_offline(domain);
3061                 }
3062                 if (cache->tdb &&
3063                         !domain->internal &&
3064                         !domain->online &&
3065                         old_status) {
3066                         centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
3067                         if (centry) {
3068                                 goto do_fetch_cache;
3069                         }
3070                 }
3071         }
3072         /* and save it */
3073         refresh_sequence_number(domain, false);
3074         if (!NT_STATUS_IS_OK(status)) {
3075                 return status;
3076         }
3077         wcache_save_password_policy(domain, status, policy);
3078
3079         return status;
3080 }
3081
3082
3083 /* Invalidate cached user and group lists coherently */
3084
3085 static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
3086                        void *state)
3087 {
3088         if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
3089             strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
3090                 tdb_delete(the_tdb, kbuf);
3091
3092         return 0;
3093 }
3094
3095 /* Invalidate the getpwnam and getgroups entries for a winbindd domain */
3096
3097 void wcache_invalidate_samlogon(struct winbindd_domain *domain, 
3098                                 const struct dom_sid *sid)
3099 {
3100         fstring key_str, sid_string;
3101         struct winbind_cache *cache;
3102
3103         /* don't clear cached U/SID and UG/SID entries when we want to logon
3104          * offline - gd */
3105
3106         if (lp_winbind_offline_logon()) {
3107                 return;
3108         }
3109
3110         if (!domain)
3111                 return;
3112
3113         cache = get_cache(domain);
3114
3115         if (!cache->tdb) {
3116                 return;
3117         }