2 Unix SMB/CIFS implementation.
4 Winbind daemon - krb5 credential cache functions
5 and in-memory cache functions.
7 Copyright (C) Guenther Deschner 2005-2006
8 Copyright (C) Jeremy Allison 2006
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 3 of the License, or
13 (at your option) any later version.
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.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #define DBGC_CLASS DBGC_WINBIND
29 /* uncomment this to do fast debugging on the krb5 ticket renewal event */
30 #ifdef DEBUG_KRB5_TKT_RENEWAL
31 #undef DEBUG_KRB5_TKT_RENEWAL
34 #define MAX_CCACHES 100
36 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
37 static void krb5_ticket_gain_handler(struct event_context *,
39 const struct timeval *,
42 /* The Krb5 ticket refresh handler should be scheduled
43 at one-half of the period from now till the tkt
45 #define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2))
47 /****************************************************************
48 Find an entry by name.
49 ****************************************************************/
51 static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
53 struct WINBINDD_CCACHE_ENTRY *entry;
55 for (entry = ccache_list; entry; entry = entry->next) {
56 if (strequal(entry->username, username)) {
63 /****************************************************************
65 ****************************************************************/
67 static int ccache_entry_count(void)
69 struct WINBINDD_CCACHE_ENTRY *entry;
72 for (entry = ccache_list; entry; entry = entry->next) {
78 /****************************************************************
79 Do the work of refreshing the ticket.
80 ****************************************************************/
82 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
83 struct timed_event *te,
84 const struct timeval *now,
87 struct WINBINDD_CCACHE_ENTRY *entry =
88 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
92 time_t expire_time = 0;
93 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
96 DEBUG(10,("krb5_ticket_refresh_handler called\n"));
97 DEBUGADD(10,("event called for: %s, %s\n",
98 entry->ccname, entry->username));
100 TALLOC_FREE(entry->event);
104 /* Kinit again if we have the user password and we can't renew the old
107 * This happens when machine are put to sleep for a very long time. */
109 if (entry->renew_until < time(NULL)) {
111 if (cred_ptr && cred_ptr->pass) {
113 set_effective_uid(entry->uid);
115 ret = kerberos_kinit_password_ext(entry->principal_name,
117 0, /* hm, can we do time correction here ? */
118 &entry->refresh_time,
121 False, /* no PAC required anymore */
123 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
125 gain_root_privilege();
128 DEBUG(3,("krb5_ticket_refresh_handler: "
129 "could not re-kinit: %s\n",
130 error_message(ret)));
131 /* destroy the ticket because we cannot rekinit
132 * it, ignore error here */
133 ads_kdestroy(entry->ccname);
135 /* Don't break the ticket refresh chain: retry
136 * refreshing ticket sometime later when KDC is
137 * unreachable -- BoYang
140 if ((ret == KRB5_KDC_UNREACH)
141 || (ret == KRB5_REALM_CANT_RESOLVE)) {
142 #if defined(DEBUG_KRB5_TKT_RENEWAL)
143 new_start = time(NULL) + 30;
145 new_start = time(NULL) +
146 MAX(30, lp_winbind_cache_time());
148 /* try to regain ticket here */
149 entry->event = event_add_timed(winbind_event_context(),
151 timeval_set(new_start, 0),
152 "krb5_ticket_gain_handler",
153 krb5_ticket_gain_handler,
157 TALLOC_FREE(entry->event);
161 DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
162 "for: %s in ccache: %s\n",
163 entry->principal_name, entry->ccname));
165 #if defined(DEBUG_KRB5_TKT_RENEWAL)
166 new_start = time(NULL) + 30;
168 /* The tkt should be refreshed at one-half the period
169 from now to the expiration time */
170 expire_time = entry->refresh_time;
171 new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time);
176 * No cached credentials
177 * destroy ticket and refresh chain
179 ads_kdestroy(entry->ccname);
180 TALLOC_FREE(entry->event);
185 set_effective_uid(entry->uid);
187 ret = smb_krb5_renew_ticket(entry->ccname,
188 entry->principal_name,
191 #if defined(DEBUG_KRB5_TKT_RENEWAL)
192 new_start = time(NULL) + 30;
194 expire_time = new_start;
195 new_start = KRB5_EVENT_REFRESH_TIME(new_start);
198 gain_root_privilege();
201 DEBUG(3,("krb5_ticket_refresh_handler: "
202 "could not renew tickets: %s\n",
203 error_message(ret)));
204 /* maybe we are beyond the renewing window */
206 /* evil rises here, we refresh ticket failed,
207 * but the ticket might be expired. Therefore,
208 * When we refresh ticket failed, destory the
211 ads_kdestroy(entry->ccname);
213 /* avoid breaking the renewal chain: retry in
214 * lp_winbind_cache_time() seconds when the KDC was not
215 * available right now.
216 * the return code can be KRB5_REALM_CANT_RESOLVE*/
218 if ((ret == KRB5_KDC_UNREACH)
219 || (ret == KRB5_REALM_CANT_RESOLVE)) {
220 #if defined(DEBUG_KRB5_TKT_RENEWAL)
221 new_start = time(NULL) + 30;
223 new_start = time(NULL) +
224 MAX(30, lp_winbind_cache_time());
226 /* ticket is destroyed here, we have to regain it
227 * if it is possible */
228 entry->event = event_add_timed(winbind_event_context(),
230 timeval_set(new_start, 0),
231 "krb5_ticket_gain_handler",
232 krb5_ticket_gain_handler,
237 /* This is evil, if the ticket was already expired.
238 * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED.
239 * But there is still a chance that we can rekinit it.
241 * This happens when user login in online mode, and then network
242 * down or something cause winbind goes offline for a very long time,
243 * and then goes online again. ticket expired, renew failed.
244 * This happens when machine are put to sleep for a long time,
245 * but shorter than entry->renew_util.
247 * Looks like the KDC is reachable, we want to rekinit as soon as
248 * possible instead of waiting some time later. */
249 if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED)
250 || (ret == KRB5_FCC_NOFILE)) goto rekinit;
256 /* in cases that ticket will be unrenewable soon, we don't try to renew ticket
257 * but try to regain ticket if it is possible */
258 if (entry->renew_until && expire_time
259 && (entry->renew_until <= expire_time)) {
260 /* try to regain ticket 10 seconds beforre expiration */
262 entry->event = event_add_timed(winbind_event_context(), entry,
263 timeval_set(expire_time, 0),
264 "krb5_ticket_gain_handler",
265 krb5_ticket_gain_handler,
270 entry->event = event_add_timed(winbind_event_context(), entry,
271 timeval_set(new_start, 0),
272 "krb5_ticket_refresh_handler",
273 krb5_ticket_refresh_handler,
279 /****************************************************************
280 Do the work of regaining a ticket when coming from offline auth.
281 ****************************************************************/
283 static void krb5_ticket_gain_handler(struct event_context *event_ctx,
284 struct timed_event *te,
285 const struct timeval *now,
288 struct WINBINDD_CCACHE_ENTRY *entry =
289 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
293 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
294 struct winbindd_domain *domain = NULL;
297 DEBUG(10,("krb5_ticket_gain_handler called\n"));
298 DEBUGADD(10,("event called for: %s, %s\n",
299 entry->ccname, entry->username));
301 TALLOC_FREE(entry->event);
305 if (!cred_ptr || !cred_ptr->pass) {
306 DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
310 if ((domain = find_domain_from_name(entry->realm)) == NULL) {
311 DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
315 if (!domain->online) {
319 set_effective_uid(entry->uid);
321 ret = kerberos_kinit_password_ext(entry->principal_name,
323 0, /* hm, can we do time correction here ? */
324 &entry->refresh_time,
327 False, /* no PAC required anymore */
329 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
331 gain_root_privilege();
334 DEBUG(3,("krb5_ticket_gain_handler: "
335 "could not kinit: %s\n",
336 error_message(ret)));
337 /* evil. If we cannot do it, destroy any the __maybe__
338 * __existing__ ticket */
339 ads_kdestroy(entry->ccname);
343 DEBUG(10,("krb5_ticket_gain_handler: "
344 "successful kinit for: %s in ccache: %s\n",
345 entry->principal_name, entry->ccname));
351 #if defined(DEBUG_KRB5_TKT_REGAIN)
352 t = timeval_set(time(NULL) + 30, 0);
354 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
357 entry->event = event_add_timed(winbind_event_context(),
360 "krb5_ticket_gain_handler",
361 krb5_ticket_gain_handler,
368 #if defined(DEBUG_KRB5_TKT_RENEWAL)
369 t = timeval_set(time(NULL) + 30, 0);
371 t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0);
374 entry->event = event_add_timed(winbind_event_context(),
377 "krb5_ticket_refresh_handler",
378 krb5_ticket_refresh_handler,
385 /****************************************************************
386 Check if an ccache entry exists.
387 ****************************************************************/
389 bool ccache_entry_exists(const char *username)
391 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
392 return (entry != NULL);
395 /****************************************************************
396 Ensure we're changing the correct entry.
397 ****************************************************************/
399 bool ccache_entry_identical(const char *username,
403 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
409 if (entry->uid != uid) {
410 DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
411 (unsigned int)entry->uid, (unsigned int)uid));
414 if (!strcsequal(entry->ccname, ccname)) {
415 DEBUG(0,("cache_entry_identical: "
416 "ccnames differ: (cache) %s != (client) %s\n",
417 entry->ccname, ccname));
423 NTSTATUS add_ccache_to_list(const char *princ_name,
426 const char *username,
432 bool postponed_request)
434 struct WINBINDD_CCACHE_ENTRY *entry = NULL;
441 if ((username == NULL && princ_name == NULL) ||
442 ccname == NULL || uid < 0) {
443 return NT_STATUS_INVALID_PARAMETER;
446 if (ccache_entry_count() + 1 > MAX_CCACHES) {
447 DEBUG(10,("add_ccache_to_list: "
448 "max number of ccaches reached\n"));
449 return NT_STATUS_NO_MORE_ENTRIES;
452 /* If it is cached login, destroy krb5 ticket
453 * to avoid surprise. */
455 if (postponed_request) {
456 /* ignore KRB5_FCC_NOFILE error here */
457 ret = ads_kdestroy(ccname);
458 if (ret == KRB5_FCC_NOFILE) {
462 DEBUG(0, ("add_ccache_to_list: failed to destroy "
463 "user krb5 ccache %s with %s\n", ccname,
464 error_message(ret)));
465 return krb5_to_nt_status(ret);
467 DEBUG(10, ("add_ccache_to_list: successfully destroyed "
468 "krb5 ccache %s for user %s\n", ccname,
474 /* Reference count old entries */
475 entry = get_ccache_by_username(username);
477 /* Check cached entries are identical. */
478 if (!ccache_entry_identical(username, uid, ccname)) {
479 return NT_STATUS_INVALID_PARAMETER;
482 DEBUG(10,("add_ccache_to_list: "
483 "ref count on entry %s is now %d\n",
484 username, entry->ref_count));
485 /* FIXME: in this case we still might want to have a krb5 cred
486 * event handler created - gd
487 * Add ticket refresh handler here */
489 if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
494 if (postponed_request) {
495 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
496 entry->event = event_add_timed(winbind_event_context(),
499 "krb5_ticket_gain_handler",
500 krb5_ticket_gain_handler,
503 /* Renew at 1/2 the ticket expiration time */
504 #if defined(DEBUG_KRB5_TKT_RENEWAL)
505 t = timeval_set(time(NULL)+30, 0);
507 t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0);
509 entry->event = event_add_timed(winbind_event_context(),
512 "krb5_ticket_refresh_handler",
513 krb5_ticket_refresh_handler,
518 ntret = remove_ccache(username);
519 if (!NT_STATUS_IS_OK(ntret)) {
520 DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 "
521 "ccache %s for user %s\n", entry->ccname,
523 DEBUG(0, ("add_ccache_to_list: error is %s\n",
527 return NT_STATUS_NO_MEMORY;
530 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
536 entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY);
538 return NT_STATUS_NO_MEMORY;
544 entry->username = talloc_strdup(entry, username);
545 if (!entry->username) {
550 entry->principal_name = talloc_strdup(entry, princ_name);
551 if (!entry->principal_name) {
556 entry->service = talloc_strdup(entry, service);
557 if (!entry->service) {
562 entry->ccname = talloc_strdup(entry, ccname);
563 if (!entry->ccname) {
567 entry->realm = talloc_strdup(entry, realm);
572 entry->create_time = create_time;
573 entry->renew_until = renew_until;
575 entry->ref_count = 1;
577 if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
581 if (postponed_request) {
582 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
583 entry->event = event_add_timed(winbind_event_context(),
586 "krb5_ticket_gain_handler",
587 krb5_ticket_gain_handler,
590 /* Renew at 1/2 the ticket expiration time */
591 #if defined(DEBUG_KRB5_TKT_RENEWAL)
592 t = timeval_set(time(NULL)+30, 0);
594 t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0);
596 entry->event = event_add_timed(winbind_event_context(),
599 "krb5_ticket_refresh_handler",
600 krb5_ticket_refresh_handler,
608 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
612 DLIST_ADD(ccache_list, entry);
614 DEBUG(10,("add_ccache_to_list: "
615 "added ccache [%s] for user [%s] to the list\n",
623 return NT_STATUS_NO_MEMORY;
626 /*******************************************************************
627 Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer
629 *******************************************************************/
631 NTSTATUS remove_ccache(const char *username)
633 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
634 NTSTATUS status = NT_STATUS_OK;
640 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
643 if (entry->ref_count <= 0) {
644 DEBUG(0,("remove_ccache: logic error. "
645 "ref count for user %s = %d\n",
646 username, entry->ref_count));
647 return NT_STATUS_INTERNAL_DB_CORRUPTION;
652 if (entry->ref_count > 0) {
653 DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
654 username, entry->ref_count));
658 /* no references any more */
660 DLIST_REMOVE(ccache_list, entry);
661 TALLOC_FREE(entry->event); /* unregisters events */
664 ret = ads_kdestroy(entry->ccname);
666 /* we ignore the error when there has been no credential cache */
667 if (ret == KRB5_FCC_NOFILE) {
670 DEBUG(0,("remove_ccache: "
671 "failed to destroy user krb5 ccache %s with: %s\n",
672 entry->ccname, error_message(ret)));
674 DEBUG(10,("remove_ccache: "
675 "successfully destroyed krb5 ccache %s for user %s\n",
676 entry->ccname, username));
678 status = krb5_to_nt_status(ret);
682 DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
687 /*******************************************************************
688 In memory credentials cache code.
689 *******************************************************************/
691 static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
693 /***********************************************************
694 Find an entry on the list by name.
695 ***********************************************************/
697 struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
699 struct WINBINDD_MEMORY_CREDS *p;
701 for (p = memory_creds_list; p; p = p->next) {
702 if (strequal(p->username, username)) {
709 /***********************************************************
710 Store the required creds and mlock them.
711 ***********************************************************/
713 static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp,
716 #if !defined(HAVE_MLOCK)
719 /* new_entry->nt_hash is the base pointer for the block
720 of memory pointed into by new_entry->lm_hash and
721 new_entry->pass (if we're storing plaintext). */
723 memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
725 memcredp->len += strlen(pass)+1;
730 /* aligning the memory on on x86_64 and compiling
731 with gcc 4.1 using -O2 causes a segv in the
732 next memset() --jerry */
733 memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
735 /* On non-linux platforms, mlock()'d memory must be aligned */
736 memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char,
737 getpagesize(), memcredp->len);
739 if (!memcredp->nt_hash) {
740 return NT_STATUS_NO_MEMORY;
742 memset(memcredp->nt_hash, 0x0, memcredp->len);
744 memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
746 #ifdef DEBUG_PASSWORD
747 DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
749 if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
750 DEBUG(0,("failed to mlock memory: %s (%d)\n",
751 strerror(errno), errno));
752 SAFE_FREE(memcredp->nt_hash);
753 return map_nt_error_from_unix(errno);
756 #ifdef DEBUG_PASSWORD
757 DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
760 /* Create and store the password hashes. */
761 E_md4hash(pass, memcredp->nt_hash);
762 E_deshash(pass, memcredp->lm_hash);
765 memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
766 memcpy(memcredp->pass, pass,
767 memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
774 /***********************************************************
775 Destroy existing creds.
776 ***********************************************************/
778 static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
780 #if !defined(HAVE_MUNLOCK)
783 if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
784 DEBUG(0,("failed to munlock memory: %s (%d)\n",
785 strerror(errno), errno));
786 return map_nt_error_from_unix(errno);
788 memset(memcredp->nt_hash, '\0', memcredp->len);
789 SAFE_FREE(memcredp->nt_hash);
790 memcredp->nt_hash = NULL;
791 memcredp->lm_hash = NULL;
792 memcredp->pass = NULL;
798 /***********************************************************
799 Replace the required creds with new ones (password change).
800 ***********************************************************/
802 static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
805 NTSTATUS status = delete_memory_creds(memcredp);
806 if (!NT_STATUS_IS_OK(status)) {
809 return store_memory_creds(memcredp, pass);
812 /*************************************************************
813 Store credentials in memory in a list.
814 *************************************************************/
816 static NTSTATUS winbindd_add_memory_creds_internal(const char *username,
820 /* Shortcut to ensure we don't store if no mlock. */
821 #if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
825 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
827 memcredp = find_memory_creds_by_name(username);
828 if (uid == (uid_t)-1) {
829 DEBUG(0,("winbindd_add_memory_creds_internal: "
830 "invalid uid for user %s.\n", username));
831 return NT_STATUS_INVALID_PARAMETER;
835 /* Already exists. Increment the reference count and replace stored creds. */
836 if (uid != memcredp->uid) {
837 DEBUG(0,("winbindd_add_memory_creds_internal: "
838 "uid %u for user %s doesn't "
839 "match stored uid %u. Replacing.\n",
840 (unsigned int)uid, username,
841 (unsigned int)memcredp->uid));
844 memcredp->ref_count++;
845 DEBUG(10,("winbindd_add_memory_creds_internal: "
846 "ref count for user %s is now %d\n",
847 username, memcredp->ref_count));
848 return winbindd_replace_memory_creds_internal(memcredp, pass);
851 memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS);
853 return NT_STATUS_NO_MEMORY;
855 memcredp->username = talloc_strdup(memcredp, username);
856 if (!memcredp->username) {
857 talloc_destroy(memcredp);
858 return NT_STATUS_NO_MEMORY;
861 status = store_memory_creds(memcredp, pass);
862 if (!NT_STATUS_IS_OK(status)) {
863 talloc_destroy(memcredp);
868 memcredp->ref_count = 1;
869 DLIST_ADD(memory_creds_list, memcredp);
871 DEBUG(10,("winbindd_add_memory_creds_internal: "
872 "added entry for user %s\n", username));
878 /*************************************************************
879 Store users credentials in memory. If we also have a
880 struct WINBINDD_CCACHE_ENTRY for this username with a
881 refresh timer, then store the plaintext of the password
882 and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY.
883 *************************************************************/
885 NTSTATUS winbindd_add_memory_creds(const char *username,
889 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
892 status = winbindd_add_memory_creds_internal(username, uid, pass);
893 if (!NT_STATUS_IS_OK(status)) {
898 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
899 memcredp = find_memory_creds_by_name(username);
901 entry->cred_ptr = memcredp;
908 /*************************************************************
909 Decrement the in-memory ref count - delete if zero.
910 *************************************************************/
912 NTSTATUS winbindd_delete_memory_creds(const char *username)
914 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
915 struct WINBINDD_CCACHE_ENTRY *entry = NULL;
916 NTSTATUS status = NT_STATUS_OK;
918 memcredp = find_memory_creds_by_name(username);
919 entry = get_ccache_by_username(username);
922 DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
924 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
927 if (memcredp->ref_count <= 0) {
928 DEBUG(0,("winbindd_delete_memory_creds: logic error. "
929 "ref count for user %s = %d\n",
930 username, memcredp->ref_count));
931 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
934 memcredp->ref_count--;
935 if (memcredp->ref_count <= 0) {
936 delete_memory_creds(memcredp);
937 DLIST_REMOVE(memory_creds_list, memcredp);
938 talloc_destroy(memcredp);
939 DEBUG(10,("winbindd_delete_memory_creds: "
940 "deleted entry for user %s\n",
943 DEBUG(10,("winbindd_delete_memory_creds: "
944 "entry for user %s ref_count now %d\n",
945 username, memcredp->ref_count));
949 /* Ensure we have no dangling references to this. */
950 entry->cred_ptr = NULL;
956 /***********************************************************
957 Replace the required creds with new ones (password change).
958 ***********************************************************/
960 NTSTATUS winbindd_replace_memory_creds(const char *username,
963 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
965 memcredp = find_memory_creds_by_name(username);
967 DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
969 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
972 DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
975 return winbindd_replace_memory_creds_internal(memcredp, pass);