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/>.
26 #include "../libcli/auth/libcli_auth.h"
30 #define DBGC_CLASS DBGC_WINBIND
32 /* uncomment this to do fast debugging on the krb5 ticket renewal event */
33 #ifdef DEBUG_KRB5_TKT_RENEWAL
34 #undef DEBUG_KRB5_TKT_RENEWAL
37 #define MAX_CCACHES 100
39 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
40 static void krb5_ticket_gain_handler(struct event_context *,
44 static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *,
47 /* The Krb5 ticket refresh handler should be scheduled
48 at one-half of the period from now till the tkt
51 static time_t krb5_event_refresh_time(time_t end_time)
53 time_t rest = end_time - time(NULL);
54 return end_time - rest/2;
57 /****************************************************************
58 Find an entry by name.
59 ****************************************************************/
61 static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
63 struct WINBINDD_CCACHE_ENTRY *entry;
65 for (entry = ccache_list; entry; entry = entry->next) {
66 if (strequal(entry->username, username)) {
73 /****************************************************************
75 ****************************************************************/
77 static int ccache_entry_count(void)
79 struct WINBINDD_CCACHE_ENTRY *entry;
82 for (entry = ccache_list; entry; entry = entry->next) {
88 void ccache_remove_all_after_fork(void)
90 struct WINBINDD_CCACHE_ENTRY *cur, *next;
92 for (cur = ccache_list; cur; cur = next) {
94 DLIST_REMOVE(ccache_list, cur);
95 TALLOC_FREE(cur->event);
102 /****************************************************************
103 Do the work of refreshing the ticket.
104 ****************************************************************/
106 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
107 struct timed_event *te,
111 struct WINBINDD_CCACHE_ENTRY *entry =
112 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
116 time_t expire_time = 0;
117 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
120 DEBUG(10,("krb5_ticket_refresh_handler called\n"));
121 DEBUGADD(10,("event called for: %s, %s\n",
122 entry->ccname, entry->username));
124 TALLOC_FREE(entry->event);
128 /* Kinit again if we have the user password and we can't renew the old
131 * This happens when machine are put to sleep for a very long time. */
133 if (entry->renew_until < time(NULL)) {
135 if (cred_ptr && cred_ptr->pass) {
137 set_effective_uid(entry->uid);
139 ret = kerberos_kinit_password_ext(entry->principal_name,
141 0, /* hm, can we do time correction here ? */
142 &entry->refresh_time,
145 False, /* no PAC required anymore */
147 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
149 gain_root_privilege();
152 DEBUG(3,("krb5_ticket_refresh_handler: "
153 "could not re-kinit: %s\n",
154 error_message(ret)));
155 /* destroy the ticket because we cannot rekinit
156 * it, ignore error here */
157 ads_kdestroy(entry->ccname);
159 /* Don't break the ticket refresh chain: retry
160 * refreshing ticket sometime later when KDC is
161 * unreachable -- BoYang. More error code handling
165 if ((ret == KRB5_KDC_UNREACH)
166 || (ret == KRB5_REALM_CANT_RESOLVE)) {
167 #if defined(DEBUG_KRB5_TKT_RENEWAL)
168 new_start = time(NULL) + 30;
170 new_start = time(NULL) +
171 MAX(30, lp_winbind_cache_time());
173 add_krb5_ticket_gain_handler_event(entry,
174 timeval_set(new_start, 0));
177 TALLOC_FREE(entry->event);
181 DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
182 "for: %s in ccache: %s\n",
183 entry->principal_name, entry->ccname));
185 #if defined(DEBUG_KRB5_TKT_RENEWAL)
186 new_start = time(NULL) + 30;
188 /* The tkt should be refreshed at one-half the period
189 from now to the expiration time */
190 expire_time = entry->refresh_time;
191 new_start = krb5_event_refresh_time(entry->refresh_time);
196 * No cached credentials
197 * destroy ticket and refresh chain
199 ads_kdestroy(entry->ccname);
200 TALLOC_FREE(entry->event);
205 set_effective_uid(entry->uid);
207 ret = smb_krb5_renew_ticket(entry->ccname,
208 entry->principal_name,
211 #if defined(DEBUG_KRB5_TKT_RENEWAL)
212 new_start = time(NULL) + 30;
214 expire_time = new_start;
215 new_start = krb5_event_refresh_time(new_start);
218 gain_root_privilege();
221 DEBUG(3,("krb5_ticket_refresh_handler: "
222 "could not renew tickets: %s\n",
223 error_message(ret)));
224 /* maybe we are beyond the renewing window */
226 /* evil rises here, we refresh ticket failed,
227 * but the ticket might be expired. Therefore,
228 * When we refresh ticket failed, destory the
231 ads_kdestroy(entry->ccname);
233 /* avoid breaking the renewal chain: retry in
234 * lp_winbind_cache_time() seconds when the KDC was not
235 * available right now.
236 * the return code can be KRB5_REALM_CANT_RESOLVE.
237 * More error code handling here? */
239 if ((ret == KRB5_KDC_UNREACH)
240 || (ret == KRB5_REALM_CANT_RESOLVE)) {
241 #if defined(DEBUG_KRB5_TKT_RENEWAL)
242 new_start = time(NULL) + 30;
244 new_start = time(NULL) +
245 MAX(30, lp_winbind_cache_time());
247 /* ticket is destroyed here, we have to regain it
248 * if it is possible */
249 add_krb5_ticket_gain_handler_event(entry,
250 timeval_set(new_start, 0));
254 /* This is evil, if the ticket was already expired.
255 * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED.
256 * But there is still a chance that we can rekinit it.
258 * This happens when user login in online mode, and then network
259 * down or something cause winbind goes offline for a very long time,
260 * and then goes online again. ticket expired, renew failed.
261 * This happens when machine are put to sleep for a long time,
262 * but shorter than entry->renew_util.
264 * Looks like the KDC is reachable, we want to rekinit as soon as
265 * possible instead of waiting some time later. */
266 if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED)
267 || (ret == KRB5_FCC_NOFILE)) goto rekinit;
273 /* in cases that ticket will be unrenewable soon, we don't try to renew ticket
274 * but try to regain ticket if it is possible */
275 if (entry->renew_until && expire_time
276 && (entry->renew_until <= expire_time)) {
277 /* try to regain ticket 10 seconds before expiration */
279 add_krb5_ticket_gain_handler_event(entry,
280 timeval_set(expire_time, 0));
284 if (entry->refresh_time == 0) {
285 entry->refresh_time = new_start;
287 entry->event = event_add_timed(winbind_event_context(), entry,
288 timeval_set(new_start, 0),
289 krb5_ticket_refresh_handler,
295 /****************************************************************
296 Do the work of regaining a ticket when coming from offline auth.
297 ****************************************************************/
299 static void krb5_ticket_gain_handler(struct event_context *event_ctx,
300 struct timed_event *te,
304 struct WINBINDD_CCACHE_ENTRY *entry =
305 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
309 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
310 struct winbindd_domain *domain = NULL;
313 DEBUG(10,("krb5_ticket_gain_handler called\n"));
314 DEBUGADD(10,("event called for: %s, %s\n",
315 entry->ccname, entry->username));
317 TALLOC_FREE(entry->event);
321 if (!cred_ptr || !cred_ptr->pass) {
322 DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
326 if ((domain = find_domain_from_name(entry->realm)) == NULL) {
327 DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
331 if (!domain->online) {
335 set_effective_uid(entry->uid);
337 ret = kerberos_kinit_password_ext(entry->principal_name,
339 0, /* hm, can we do time correction here ? */
340 &entry->refresh_time,
343 False, /* no PAC required anymore */
345 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
347 gain_root_privilege();
350 DEBUG(3,("krb5_ticket_gain_handler: "
351 "could not kinit: %s\n",
352 error_message(ret)));
353 /* evil. If we cannot do it, destroy any the __maybe__
354 * __existing__ ticket */
355 ads_kdestroy(entry->ccname);
359 DEBUG(10,("krb5_ticket_gain_handler: "
360 "successful kinit for: %s in ccache: %s\n",
361 entry->principal_name, entry->ccname));
367 #if defined(DEBUG_KRB5_TKT_RENEWAL)
368 t = timeval_set(time(NULL) + 30, 0);
370 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
373 add_krb5_ticket_gain_handler_event(entry, t);
378 #if defined(DEBUG_KRB5_TKT_RENEWAL)
379 t = timeval_set(time(NULL) + 30, 0);
381 t = timeval_set(krb5_event_refresh_time(entry->refresh_time), 0);
384 if (entry->refresh_time == 0) {
385 entry->refresh_time = t.tv_sec;
387 entry->event = event_add_timed(winbind_event_context(),
390 krb5_ticket_refresh_handler,
397 /**************************************************************
398 The gain initial ticket case is recognised as entry->refresh_time
400 **************************************************************/
402 static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry,
405 entry->refresh_time = 0;
406 entry->event = event_add_timed(winbind_event_context(),
409 krb5_ticket_gain_handler,
413 void ccache_regain_all_now(void)
415 struct WINBINDD_CCACHE_ENTRY *cur;
416 struct timeval t = timeval_current();
418 for (cur = ccache_list; cur; cur = cur->next) {
419 struct timed_event *new_event;
422 * if refresh_time is 0, we know that the
423 * the event has the krb5_ticket_gain_handler
425 if (cur->refresh_time == 0) {
426 new_event = event_add_timed(winbind_event_context(),
429 krb5_ticket_gain_handler,
432 new_event = event_add_timed(winbind_event_context(),
435 krb5_ticket_refresh_handler,
443 TALLOC_FREE(cur->event);
444 cur->event = new_event;
450 /****************************************************************
451 Check if an ccache entry exists.
452 ****************************************************************/
454 bool ccache_entry_exists(const char *username)
456 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
457 return (entry != NULL);
460 /****************************************************************
461 Ensure we're changing the correct entry.
462 ****************************************************************/
464 bool ccache_entry_identical(const char *username,
468 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
474 if (entry->uid != uid) {
475 DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
476 (unsigned int)entry->uid, (unsigned int)uid));
479 if (!strcsequal(entry->ccname, ccname)) {
480 DEBUG(0,("cache_entry_identical: "
481 "ccnames differ: (cache) %s != (client) %s\n",
482 entry->ccname, ccname));
488 NTSTATUS add_ccache_to_list(const char *princ_name,
491 const char *username,
497 bool postponed_request)
499 struct WINBINDD_CCACHE_ENTRY *entry = NULL;
506 if ((username == NULL && princ_name == NULL) ||
507 ccname == NULL || uid < 0) {
508 return NT_STATUS_INVALID_PARAMETER;
511 if (ccache_entry_count() + 1 > MAX_CCACHES) {
512 DEBUG(10,("add_ccache_to_list: "
513 "max number of ccaches reached\n"));
514 return NT_STATUS_NO_MORE_ENTRIES;
517 /* If it is cached login, destroy krb5 ticket
518 * to avoid surprise. */
520 if (postponed_request) {
521 /* ignore KRB5_FCC_NOFILE error here */
522 ret = ads_kdestroy(ccname);
523 if (ret == KRB5_FCC_NOFILE) {
527 DEBUG(0, ("add_ccache_to_list: failed to destroy "
528 "user krb5 ccache %s with %s\n", ccname,
529 error_message(ret)));
530 return krb5_to_nt_status(ret);
532 DEBUG(10, ("add_ccache_to_list: successfully destroyed "
533 "krb5 ccache %s for user %s\n", ccname,
538 /* Reference count old entries */
539 entry = get_ccache_by_username(username);
541 /* Check cached entries are identical. */
542 if (!ccache_entry_identical(username, uid, ccname)) {
543 return NT_STATUS_INVALID_PARAMETER;
546 DEBUG(10,("add_ccache_to_list: "
547 "ref count on entry %s is now %d\n",
548 username, entry->ref_count));
549 /* FIXME: in this case we still might want to have a krb5 cred
550 * event handler created - gd
551 * Add ticket refresh handler here */
553 if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
558 if (postponed_request) {
559 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
560 add_krb5_ticket_gain_handler_event(entry, t);
562 /* Renew at 1/2 the ticket expiration time */
563 #if defined(DEBUG_KRB5_TKT_RENEWAL)
564 t = timeval_set(time(NULL)+30, 0);
566 t = timeval_set(krb5_event_refresh_time(ticket_end),
569 if (!entry->refresh_time) {
570 entry->refresh_time = t.tv_sec;
572 entry->event = event_add_timed(winbind_event_context(),
575 krb5_ticket_refresh_handler,
580 ntret = remove_ccache(username);
581 if (!NT_STATUS_IS_OK(ntret)) {
582 DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 "
583 "ccache %s for user %s\n", entry->ccname,
585 DEBUG(0, ("add_ccache_to_list: error is %s\n",
589 return NT_STATUS_NO_MEMORY;
592 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
598 entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY);
600 return NT_STATUS_NO_MEMORY;
606 entry->username = talloc_strdup(entry, username);
607 if (!entry->username) {
612 entry->principal_name = talloc_strdup(entry, princ_name);
613 if (!entry->principal_name) {
618 entry->service = talloc_strdup(entry, service);
619 if (!entry->service) {
624 entry->ccname = talloc_strdup(entry, ccname);
625 if (!entry->ccname) {
629 entry->realm = talloc_strdup(entry, realm);
634 entry->create_time = create_time;
635 entry->renew_until = renew_until;
637 entry->ref_count = 1;
639 if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
643 if (postponed_request) {
644 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
645 add_krb5_ticket_gain_handler_event(entry, t);
647 /* Renew at 1/2 the ticket expiration time */
648 #if defined(DEBUG_KRB5_TKT_RENEWAL)
649 t = timeval_set(time(NULL)+30, 0);
651 t = timeval_set(krb5_event_refresh_time(ticket_end), 0);
653 if (entry->refresh_time == 0) {
654 entry->refresh_time = t.tv_sec;
656 entry->event = event_add_timed(winbind_event_context(),
659 krb5_ticket_refresh_handler,
667 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
671 DLIST_ADD(ccache_list, entry);
673 DEBUG(10,("add_ccache_to_list: "
674 "added ccache [%s] for user [%s] to the list\n",
682 return NT_STATUS_NO_MEMORY;
685 /*******************************************************************
686 Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer
688 *******************************************************************/
690 NTSTATUS remove_ccache(const char *username)
692 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
693 NTSTATUS status = NT_STATUS_OK;
699 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
702 if (entry->ref_count <= 0) {
703 DEBUG(0,("remove_ccache: logic error. "
704 "ref count for user %s = %d\n",
705 username, entry->ref_count));
706 return NT_STATUS_INTERNAL_DB_CORRUPTION;
711 if (entry->ref_count > 0) {
712 DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
713 username, entry->ref_count));
717 /* no references any more */
719 DLIST_REMOVE(ccache_list, entry);
720 TALLOC_FREE(entry->event); /* unregisters events */
723 ret = ads_kdestroy(entry->ccname);
725 /* we ignore the error when there has been no credential cache */
726 if (ret == KRB5_FCC_NOFILE) {
729 DEBUG(0,("remove_ccache: "
730 "failed to destroy user krb5 ccache %s with: %s\n",
731 entry->ccname, error_message(ret)));
733 DEBUG(10,("remove_ccache: "
734 "successfully destroyed krb5 ccache %s for user %s\n",
735 entry->ccname, username));
737 status = krb5_to_nt_status(ret);
741 DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
746 /*******************************************************************
747 In memory credentials cache code.
748 *******************************************************************/
750 static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
752 /***********************************************************
753 Find an entry on the list by name.
754 ***********************************************************/
756 struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
758 struct WINBINDD_MEMORY_CREDS *p;
760 for (p = memory_creds_list; p; p = p->next) {
761 if (strequal(p->username, username)) {
768 /***********************************************************
769 Store the required creds and mlock them.
770 ***********************************************************/
772 static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp,
775 #if !defined(HAVE_MLOCK)
778 /* new_entry->nt_hash is the base pointer for the block
779 of memory pointed into by new_entry->lm_hash and
780 new_entry->pass (if we're storing plaintext). */
782 memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
784 memcredp->len += strlen(pass)+1;
789 /* aligning the memory on on x86_64 and compiling
790 with gcc 4.1 using -O2 causes a segv in the
791 next memset() --jerry */
792 memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
794 /* On non-linux platforms, mlock()'d memory must be aligned */
795 memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char,
796 getpagesize(), memcredp->len);
798 if (!memcredp->nt_hash) {
799 return NT_STATUS_NO_MEMORY;
801 memset(memcredp->nt_hash, 0x0, memcredp->len);
803 memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
805 #ifdef DEBUG_PASSWORD
806 DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
808 if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
809 DEBUG(0,("failed to mlock memory: %s (%d)\n",
810 strerror(errno), errno));
811 SAFE_FREE(memcredp->nt_hash);
812 return map_nt_error_from_unix(errno);
815 #ifdef DEBUG_PASSWORD
816 DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
819 /* Create and store the password hashes. */
820 E_md4hash(pass, memcredp->nt_hash);
821 E_deshash(pass, memcredp->lm_hash);
824 memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
825 memcpy(memcredp->pass, pass,
826 memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
833 /***********************************************************
834 Destroy existing creds.
835 ***********************************************************/
837 static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
839 #if !defined(HAVE_MUNLOCK)
842 if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
843 DEBUG(0,("failed to munlock memory: %s (%d)\n",
844 strerror(errno), errno));
845 return map_nt_error_from_unix(errno);
847 memset(memcredp->nt_hash, '\0', memcredp->len);
848 SAFE_FREE(memcredp->nt_hash);
849 memcredp->nt_hash = NULL;
850 memcredp->lm_hash = NULL;
851 memcredp->pass = NULL;
857 /***********************************************************
858 Replace the required creds with new ones (password change).
859 ***********************************************************/
861 static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
864 NTSTATUS status = delete_memory_creds(memcredp);
865 if (!NT_STATUS_IS_OK(status)) {
868 return store_memory_creds(memcredp, pass);
871 /*************************************************************
872 Store credentials in memory in a list.
873 *************************************************************/
875 static NTSTATUS winbindd_add_memory_creds_internal(const char *username,
879 /* Shortcut to ensure we don't store if no mlock. */
880 #if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
884 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
886 memcredp = find_memory_creds_by_name(username);
887 if (uid == (uid_t)-1) {
888 DEBUG(0,("winbindd_add_memory_creds_internal: "
889 "invalid uid for user %s.\n", username));
890 return NT_STATUS_INVALID_PARAMETER;
894 /* Already exists. Increment the reference count and replace stored creds. */
895 if (uid != memcredp->uid) {
896 DEBUG(0,("winbindd_add_memory_creds_internal: "
897 "uid %u for user %s doesn't "
898 "match stored uid %u. Replacing.\n",
899 (unsigned int)uid, username,
900 (unsigned int)memcredp->uid));
903 memcredp->ref_count++;
904 DEBUG(10,("winbindd_add_memory_creds_internal: "
905 "ref count for user %s is now %d\n",
906 username, memcredp->ref_count));
907 return winbindd_replace_memory_creds_internal(memcredp, pass);
910 memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS);
912 return NT_STATUS_NO_MEMORY;
914 memcredp->username = talloc_strdup(memcredp, username);
915 if (!memcredp->username) {
916 talloc_destroy(memcredp);
917 return NT_STATUS_NO_MEMORY;
920 status = store_memory_creds(memcredp, pass);
921 if (!NT_STATUS_IS_OK(status)) {
922 talloc_destroy(memcredp);
927 memcredp->ref_count = 1;
928 DLIST_ADD(memory_creds_list, memcredp);
930 DEBUG(10,("winbindd_add_memory_creds_internal: "
931 "added entry for user %s\n", username));
937 /*************************************************************
938 Store users credentials in memory. If we also have a
939 struct WINBINDD_CCACHE_ENTRY for this username with a
940 refresh timer, then store the plaintext of the password
941 and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY.
942 *************************************************************/
944 NTSTATUS winbindd_add_memory_creds(const char *username,
948 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
951 status = winbindd_add_memory_creds_internal(username, uid, pass);
952 if (!NT_STATUS_IS_OK(status)) {
957 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
958 memcredp = find_memory_creds_by_name(username);
960 entry->cred_ptr = memcredp;
967 /*************************************************************
968 Decrement the in-memory ref count - delete if zero.
969 *************************************************************/
971 NTSTATUS winbindd_delete_memory_creds(const char *username)
973 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
974 struct WINBINDD_CCACHE_ENTRY *entry = NULL;
975 NTSTATUS status = NT_STATUS_OK;
977 memcredp = find_memory_creds_by_name(username);
978 entry = get_ccache_by_username(username);
981 DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
983 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
986 if (memcredp->ref_count <= 0) {
987 DEBUG(0,("winbindd_delete_memory_creds: logic error. "
988 "ref count for user %s = %d\n",
989 username, memcredp->ref_count));
990 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
993 memcredp->ref_count--;
994 if (memcredp->ref_count <= 0) {
995 delete_memory_creds(memcredp);
996 DLIST_REMOVE(memory_creds_list, memcredp);
997 talloc_destroy(memcredp);
998 DEBUG(10,("winbindd_delete_memory_creds: "
999 "deleted entry for user %s\n",
1002 DEBUG(10,("winbindd_delete_memory_creds: "
1003 "entry for user %s ref_count now %d\n",
1004 username, memcredp->ref_count));
1008 /* Ensure we have no dangling references to this. */
1009 entry->cred_ptr = NULL;
1015 /***********************************************************
1016 Replace the required creds with new ones (password change).
1017 ***********************************************************/
1019 NTSTATUS winbindd_replace_memory_creds(const char *username,
1022 struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
1024 memcredp = find_memory_creds_by_name(username);
1026 DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
1028 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1031 DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
1034 return winbindd_replace_memory_creds_internal(memcredp, pass);