1/* 2 Unix SMB/CIFS implementation. 3 4 Winbind daemon - krb5 credential cache functions 5 and in-memory cache functions. 6 7 Copyright (C) Guenther Deschner 2005-2006 8 Copyright (C) Jeremy Allison 2006 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23*/ 24 25#include "includes.h" 26#include "winbindd.h" 27#undef DBGC_CLASS 28#define DBGC_CLASS DBGC_WINBIND 29 30/* uncomment this to to fast debugging on the krb5 ticket renewal event */ 31#ifdef DEBUG_KRB5_TKT_RENEWAL 32#undef DEBUG_KRB5_TKT_RENEWAL 33#endif 34 35#define MAX_CCACHES 100 36 37static struct WINBINDD_CCACHE_ENTRY *ccache_list; 38 39/* The Krb5 ticket refresh handler should be scheduled 40 at one-half of the period from now till the tkt 41 expiration */ 42#define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2)) 43 44/**************************************************************** 45 Find an entry by name. 46****************************************************************/ 47 48static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username) 49{ 50 struct WINBINDD_CCACHE_ENTRY *entry; 51 52 for (entry = ccache_list; entry; entry = entry->next) { 53 if (strequal(entry->username, username)) { 54 return entry; 55 } 56 } 57 return NULL; 58} 59 60/**************************************************************** 61 How many do we have ? 62****************************************************************/ 63 64static int ccache_entry_count(void) 65{ 66 struct WINBINDD_CCACHE_ENTRY *entry; 67 int i = 0; 68 69 for (entry = ccache_list; entry; entry = entry->next) { 70 i++; 71 } 72 return i; 73} 74 75/**************************************************************** 76 Do the work of refreshing the ticket. 77****************************************************************/ 78 79static void krb5_ticket_refresh_handler(struct event_context *event_ctx, 80 struct timed_event *te, 81 const struct timeval *now, 82 void *private_data) 83{ 84 struct WINBINDD_CCACHE_ENTRY *entry = 85 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); 86#ifdef HAVE_KRB5 87 int ret; 88 time_t new_start; 89 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; 90#endif 91 92 DEBUG(10,("krb5_ticket_refresh_handler called\n")); 93 DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username)); 94 95 TALLOC_FREE(entry->event); 96 97#ifdef HAVE_KRB5 98 99 /* Kinit again if we have the user password and we can't renew the old 100 * tgt anymore */ 101 102 if ((entry->renew_until < time(NULL)) && cred_ptr && cred_ptr->pass) { 103 104 set_effective_uid(entry->uid); 105 106 ret = kerberos_kinit_password_ext(entry->principal_name, 107 cred_ptr->pass, 108 0, /* hm, can we do time correction here ? */ 109 &entry->refresh_time, 110 &entry->renew_until, 111 entry->ccname, 112 False, /* no PAC required anymore */ 113 True, 114 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME); 115 gain_root_privilege(); 116 117 if (ret) { 118 DEBUG(3,("krb5_ticket_refresh_handler: could not re-kinit: %s\n", 119 error_message(ret))); 120 TALLOC_FREE(entry->event); 121 return; 122 } 123 124 DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit " 125 "for: %s in ccache: %s\n", 126 entry->principal_name, entry->ccname)); 127 128#if defined(DEBUG_KRB5_TKT_RENEWAL) 129 new_start = time(NULL) + 30; 130#else 131 /* The tkt should be refreshed at one-half the period 132 from now to the expiration time */ 133 new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time); 134#endif 135 136 goto done; 137 } 138 139 set_effective_uid(entry->uid); 140 141 ret = smb_krb5_renew_ticket(entry->ccname, 142 entry->principal_name, 143 entry->service, 144 &new_start); 145#if defined(DEBUG_KRB5_TKT_RENEWAL) 146 new_start = time(NULL) + 30; 147#else 148 new_start = KRB5_EVENT_REFRESH_TIME(new_start); 149#endif 150 151 gain_root_privilege(); 152 153 if (ret) { 154 DEBUG(3,("krb5_ticket_refresh_handler: could not renew tickets: %s\n", 155 error_message(ret))); 156 /* maybe we are beyond the renewing window */ 157 158 /* avoid breaking the renewal chain: retry in lp_winbind_cache_time() 159 * seconds when the KDC was not available right now. */ 160 161 if (ret == KRB5_KDC_UNREACH) { 162 new_start = time(NULL) + MAX(30, lp_winbind_cache_time()); 163 goto done; 164 } 165 166 return; 167 } 168 169done: 170 171 entry->event = event_add_timed(winbind_event_context(), entry, 172 timeval_set(new_start, 0), 173 "krb5_ticket_refresh_handler", 174 krb5_ticket_refresh_handler, 175 entry); 176 177#endif 178} 179 180/**************************************************************** 181 Do the work of regaining a ticket when coming from offline auth. 182****************************************************************/ 183 184static void krb5_ticket_gain_handler(struct event_context *event_ctx, 185 struct timed_event *te, 186 const struct timeval *now, 187 void *private_data) 188{ 189 struct WINBINDD_CCACHE_ENTRY *entry = 190 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); 191#ifdef HAVE_KRB5 192 int ret; 193 struct timeval t; 194 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; 195 struct winbindd_domain *domain = NULL; 196#endif 197 198 DEBUG(10,("krb5_ticket_gain_handler called\n")); 199 DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username)); 200 201 TALLOC_FREE(entry->event); 202 203#ifdef HAVE_KRB5 204 205 if (!cred_ptr || !cred_ptr->pass) { 206 DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n")); 207 return; 208 } 209 210 if ((domain = find_domain_from_name(entry->realm)) == NULL) { 211 DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n")); 212 return; 213 } 214 215 if (domain->online) { 216 217 set_effective_uid(entry->uid); 218 219 ret = kerberos_kinit_password_ext(entry->principal_name, 220 cred_ptr->pass, 221 0, /* hm, can we do time correction here ? */ 222 &entry->refresh_time, 223 &entry->renew_until, 224 entry->ccname, 225 False, /* no PAC required anymore */ 226 True, 227 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME); 228 gain_root_privilege(); 229 230 if (ret) { 231 DEBUG(3,("krb5_ticket_gain_handler: could not kinit: %s\n", 232 error_message(ret))); 233 goto retry_later; 234 } 235 236 DEBUG(10,("krb5_ticket_gain_handler: successful kinit for: %s in ccache: %s\n", 237 entry->principal_name, entry->ccname)); 238 239 goto got_ticket; 240 } 241 242 retry_later: 243 244 entry->event = event_add_timed(winbind_event_context(), entry, 245 timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0), 246 "krb5_ticket_gain_handler", 247 krb5_ticket_gain_handler, 248 entry); 249 250 return; 251 252 got_ticket: 253 254#if defined(DEBUG_KRB5_TKT_RENEWAL) 255 t = timeval_set(time(NULL) + 30, 0); 256#else 257 t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0); 258#endif 259 260 entry->event = event_add_timed(winbind_event_context(), entry, 261 t, 262 "krb5_ticket_refresh_handler", 263 krb5_ticket_refresh_handler, 264 entry); 265 266 return; 267#endif 268} 269 270/**************************************************************** 271 Check if an ccache entry exists. 272****************************************************************/ 273 274BOOL ccache_entry_exists(const char *username) 275{ 276 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); 277 return (entry != NULL); 278} 279 280/**************************************************************** 281 Ensure we're changing the correct entry. 282****************************************************************/ 283 284BOOL ccache_entry_identical(const char *username, uid_t uid, const char *ccname) 285{ 286 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); 287 288 if (!entry) { 289 return False; 290 } 291 292 if (entry->uid != uid) { 293 DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n", 294 (unsigned int)entry->uid, (unsigned int)uid )); 295 return False; 296 } 297 if (!strcsequal(entry->ccname, ccname)) { 298 DEBUG(0,("cache_entry_identical: ccnames differ: (cache) %s != (client) %s\n", 299 entry->ccname, ccname)); 300 return False; 301 } 302 return True; 303} 304 305NTSTATUS add_ccache_to_list(const char *princ_name, 306 const char *ccname, 307 const char *service, 308 const char *username, 309 const char *realm, 310 uid_t uid, 311 time_t create_time, 312 time_t ticket_end, 313 time_t renew_until, 314 BOOL postponed_request) 315{ 316 struct WINBINDD_CCACHE_ENTRY *entry = NULL; 317 318 if ((username == NULL && princ_name == NULL) || ccname == NULL || uid < 0) { 319 return NT_STATUS_INVALID_PARAMETER; 320 } 321 322 if (ccache_entry_count() + 1 > MAX_CCACHES) { 323 DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n")); 324 return NT_STATUS_NO_MORE_ENTRIES; 325 } 326 327 /* Reference count old entries */ 328 entry = get_ccache_by_username(username); 329 if (entry) { 330 /* Check cached entries are identical. */ 331 if (!ccache_entry_identical(username, uid, ccname)) { 332 return NT_STATUS_INVALID_PARAMETER; 333 } 334 entry->ref_count++; 335 DEBUG(10,("add_ccache_to_list: ref count on entry %s is now %d\n", 336 username, entry->ref_count)); 337 /* FIXME: in this case we still might want to have a krb5 cred 338 * event handler created - gd*/ 339 return NT_STATUS_OK; 340 } 341 342 entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY); 343 if (!entry) { 344 return NT_STATUS_NO_MEMORY; 345 } 346 347 ZERO_STRUCTP(entry); 348 349 if (username) { 350 entry->username = talloc_strdup(entry, username); 351 if (!entry->username) { 352 goto no_mem; 353 } 354 } 355 if (princ_name) { 356 entry->principal_name = talloc_strdup(entry, princ_name); 357 if (!entry->principal_name) { 358 goto no_mem; 359 } 360 } 361 if (service) { 362 entry->service = talloc_strdup(entry, service); 363 if (!entry->service) { 364 goto no_mem; 365 } 366 } 367 368 entry->ccname = talloc_strdup(entry, ccname); 369 if (!entry->ccname) { 370 goto no_mem; 371 } 372 373 entry->realm = talloc_strdup(entry, realm); 374 if (!entry->realm) { 375 goto no_mem; 376 } 377 378 entry->create_time = create_time; 379 entry->renew_until = renew_until; 380 entry->uid = uid; 381 entry->ref_count = 1; 382 383 if (lp_winbind_refresh_tickets() && renew_until > 0) { 384 if (postponed_request) { 385 entry->event = event_add_timed(winbind_event_context(), entry, 386 timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0), 387 "krb5_ticket_gain_handler", 388 krb5_ticket_gain_handler, 389 entry); 390 } else { 391 /* Renew at 1/2 the ticket expiration time */ 392 entry->event = event_add_timed(winbind_event_context(), entry, 393#if defined(DEBUG_KRB5_TKT_RENEWAL) 394 timeval_set(time(NULL)+30, 0), 395#else 396 timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0), 397#endif 398 "krb5_ticket_refresh_handler", 399 krb5_ticket_refresh_handler, 400 entry); 401 } 402 403 if (!entry->event) { 404 goto no_mem; 405 } 406 407 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n")); 408 } 409 410 DLIST_ADD(ccache_list, entry); 411 412 DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username)); 413 414 return NT_STATUS_OK; 415 416 no_mem: 417 418 TALLOC_FREE(entry); 419 return NT_STATUS_NO_MEMORY; 420} 421 422/******************************************************************* 423 Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer referenced. 424*******************************************************************/ 425 426NTSTATUS remove_ccache(const char *username) 427{ 428 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); 429 NTSTATUS status; 430#ifdef HAVE_KRB5 431 krb5_error_code ret; 432#endif 433 434 if (!entry) { 435 return NT_STATUS_OBJECT_NAME_NOT_FOUND; 436 } 437 438 if (entry->ref_count <= 0) { 439 DEBUG(0,("remove_ccache: logic error. ref count for user %s = %d\n", 440 username, entry->ref_count)); 441 return NT_STATUS_INTERNAL_DB_CORRUPTION; 442 } 443 444 entry->ref_count--; 445 446 if (entry->ref_count > 0) { 447 DEBUG(10,("remove_ccache: entry %s ref count now %d\n", 448 username, entry->ref_count )); 449 return NT_STATUS_OK; 450 } 451 452 /* no references any more */ 453 454 DLIST_REMOVE(ccache_list, entry); 455 TALLOC_FREE(entry->event); /* unregisters events */ 456 457#ifdef HAVE_KRB5 458 ret = ads_kdestroy(entry->ccname); 459 460 /* we ignore the error when there has been no credential cache */ 461 if (ret == KRB5_FCC_NOFILE) { 462 ret = 0; 463 } else if (ret) { 464 DEBUG(0,("remove_ccache: failed to destroy user krb5 ccache %s with: %s\n", 465 entry->ccname, error_message(ret))); 466 } else { 467 DEBUG(10,("remove_ccache: successfully destroyed krb5 ccache %s for user %s\n", 468 entry->ccname, username)); 469 } 470 status = krb5_to_nt_status(ret); 471#endif 472 473 TALLOC_FREE(entry); 474 DEBUG(10,("remove_ccache: removed ccache for user %s\n", username)); 475 476 return status; 477} 478 479/******************************************************************* 480 In memory credentials cache code. 481*******************************************************************/ 482 483static struct WINBINDD_MEMORY_CREDS *memory_creds_list; 484 485/*********************************************************** 486 Find an entry on the list by name. 487***********************************************************/ 488 489struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username) 490{ 491 struct WINBINDD_MEMORY_CREDS *p; 492 493 for (p = memory_creds_list; p; p = p->next) { 494 if (strequal(p->username, username)) { 495 return p; 496 } 497 } 498 return NULL; 499} 500 501/*********************************************************** 502 Store the required creds and mlock them. 503***********************************************************/ 504 505static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, const char *pass) 506{ 507#if !defined(HAVE_MLOCK) 508 return NT_STATUS_OK; 509#else 510 /* new_entry->nt_hash is the base pointer for the block 511 of memory pointed into by new_entry->lm_hash and 512 new_entry->pass (if we're storing plaintext). */ 513 514 memcredp->len = NT_HASH_LEN + LM_HASH_LEN; 515 if (pass) { 516 memcredp->len += strlen(pass)+1; 517 } 518 519 520#if defined(LINUX) 521 /* aligning the memory on on x86_64 and compiling 522 with gcc 4.1 using -O2 causes a segv in the 523 next memset() --jerry */ 524 memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len); 525#else 526 /* On non-linux platforms, mlock()'d memory must be aligned */ 527 memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char, 528 getpagesize(), memcredp->len); 529#endif 530 if (!memcredp->nt_hash) { 531 return NT_STATUS_NO_MEMORY; 532 } 533 memset( memcredp->nt_hash, 0x0, memcredp->len ); 534 535 memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN; 536 537#ifdef DEBUG_PASSWORD 538 DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash)); 539#endif 540 if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) { 541 DEBUG(0,("failed to mlock memory: %s (%d)\n", 542 strerror(errno), errno)); 543 SAFE_FREE(memcredp->nt_hash); 544 return map_nt_error_from_unix(errno); 545 } 546 547#ifdef DEBUG_PASSWORD 548 DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash)); 549#endif 550 551 /* Create and store the password hashes. */ 552 E_md4hash(pass, memcredp->nt_hash); 553 E_deshash(pass, memcredp->lm_hash); 554 555 if (pass) { 556 memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN; 557 memcpy(memcredp->pass, pass, memcredp->len - NT_HASH_LEN - LM_HASH_LEN); 558 } 559 560 return NT_STATUS_OK; 561#endif 562} 563 564/*********************************************************** 565 Destroy existing creds. 566***********************************************************/ 567 568static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp) 569{ 570#if !defined(HAVE_MUNLOCK) 571 return NT_STATUS_OK; 572#else 573 if (munlock(memcredp->nt_hash, memcredp->len) == -1) { 574 DEBUG(0,("failed to munlock memory: %s (%d)\n", 575 strerror(errno), errno)); 576 return map_nt_error_from_unix(errno); 577 } 578 memset(memcredp->nt_hash, '\0', memcredp->len); 579 SAFE_FREE(memcredp->nt_hash); 580 memcredp->nt_hash = NULL; 581 memcredp->lm_hash = NULL; 582 memcredp->pass = NULL; 583 memcredp->len = 0; 584 return NT_STATUS_OK; 585#endif 586} 587 588/*********************************************************** 589 Replace the required creds with new ones (password change). 590***********************************************************/ 591 592static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp, 593 const char *pass) 594{ 595 NTSTATUS status = delete_memory_creds(memcredp); 596 if (!NT_STATUS_IS_OK(status)) { 597 return status; 598 } 599 return store_memory_creds(memcredp, pass); 600} 601 602/************************************************************* 603 Store credentials in memory in a list. 604*************************************************************/ 605 606static NTSTATUS winbindd_add_memory_creds_internal(const char *username, uid_t uid, const char *pass) 607{ 608 /* Shortcut to ensure we don't store if no mlock. */ 609#if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK) 610 return NT_STATUS_OK; 611#else 612 NTSTATUS status; 613 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); 614 615 if (uid == (uid_t)-1) { 616 DEBUG(0,("winbindd_add_memory_creds_internal: invalid uid for user %s.\n", 617 username )); 618 return NT_STATUS_INVALID_PARAMETER; 619 } 620 621 if (memcredp) { 622 /* Already exists. Increment the reference count and replace stored creds. */ 623 if (uid != memcredp->uid) { 624 DEBUG(0,("winbindd_add_memory_creds_internal: uid %u for user %s doesn't " 625 "match stored uid %u. Replacing.\n", 626 (unsigned int)uid, username, (unsigned int)memcredp->uid )); 627 memcredp->uid = uid; 628 } 629 memcredp->ref_count++; 630 DEBUG(10,("winbindd_add_memory_creds_internal: ref count for user %s is now %d\n", 631 username, memcredp->ref_count )); 632 return winbindd_replace_memory_creds_internal(memcredp, pass); 633 } 634 635 memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS); 636 if (!memcredp) { 637 return NT_STATUS_NO_MEMORY; 638 } 639 memcredp->username = talloc_strdup(memcredp, username); 640 if (!memcredp->username) { 641 talloc_destroy(memcredp); 642 return NT_STATUS_NO_MEMORY; 643 } 644 645 status = store_memory_creds(memcredp, pass); 646 if (!NT_STATUS_IS_OK(status)) { 647 talloc_destroy(memcredp); 648 return status; 649 } 650 651 memcredp->uid = uid; 652 memcredp->ref_count = 1; 653 DLIST_ADD(memory_creds_list, memcredp); 654 655 DEBUG(10,("winbindd_add_memory_creds_internal: added entry for user %s\n", 656 username )); 657 658 return NT_STATUS_OK; 659#endif 660} 661 662/************************************************************* 663 Store users credentials in memory. If we also have a 664 struct WINBINDD_CCACHE_ENTRY for this username with a 665 refresh timer, then store the plaintext of the password 666 and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY. 667*************************************************************/ 668 669NTSTATUS winbindd_add_memory_creds(const char *username, uid_t uid, const char *pass) 670{ 671 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); 672 NTSTATUS status; 673 674 status = winbindd_add_memory_creds_internal(username, uid, pass); 675 if (!NT_STATUS_IS_OK(status)) { 676 return status; 677 } 678 679 if (entry) { 680 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); 681 if (memcredp) { 682 entry->cred_ptr = memcredp; 683 } 684 } 685 686 return status; 687} 688 689/************************************************************* 690 Decrement the in-memory ref count - delete if zero. 691*************************************************************/ 692 693NTSTATUS winbindd_delete_memory_creds(const char *username) 694{ 695 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); 696 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); 697 NTSTATUS status = NT_STATUS_OK; 698 699 if (!memcredp) { 700 DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n", 701 username )); 702 return NT_STATUS_OBJECT_NAME_NOT_FOUND; 703 } 704 705 if (memcredp->ref_count <= 0) { 706 DEBUG(0,("winbindd_delete_memory_creds: logic error. ref count for user %s = %d\n", 707 username, memcredp->ref_count)); 708 status = NT_STATUS_INTERNAL_DB_CORRUPTION; 709 } 710 711 memcredp->ref_count--; 712 if (memcredp->ref_count <= 0) { 713 delete_memory_creds(memcredp); 714 DLIST_REMOVE(memory_creds_list, memcredp); 715 talloc_destroy(memcredp); 716 DEBUG(10,("winbindd_delete_memory_creds: deleted entry for user %s\n", 717 username)); 718 } else { 719 DEBUG(10,("winbindd_delete_memory_creds: entry for user %s ref_count now %d\n", 720 username, memcredp->ref_count)); 721 } 722 723 if (entry) { 724 entry->cred_ptr = NULL; /* Ensure we have no dangling references to this. */ 725 } 726 return status; 727} 728 729/*********************************************************** 730 Replace the required creds with new ones (password change). 731***********************************************************/ 732 733NTSTATUS winbindd_replace_memory_creds(const char *username, const char *pass) 734{ 735 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); 736 737 if (!memcredp) { 738 DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n", 739 username )); 740 return NT_STATUS_OBJECT_NAME_NOT_FOUND; 741 } 742 743 DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n", 744 username )); 745 746 return winbindd_replace_memory_creds_internal(memcredp, pass); 747} 748