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