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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #define DBGC_CLASS DBGC_WINBIND
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
35 #define MAX_CCACHES 100
37 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
38 static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry,
41 /* The Krb5 ticket refresh handler should be scheduled
42 at one-half of the period from now till the tkt
44 #define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2))
46 /****************************************************************
47 Find an entry by name.
48 ****************************************************************/
50 static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
52 struct WINBINDD_CCACHE_ENTRY *entry;
54 for (entry = ccache_list; entry; entry = entry->next) {
55 if (strequal(entry->username, username)) {
62 /****************************************************************
64 ****************************************************************/
66 static int ccache_entry_count(void)
68 struct WINBINDD_CCACHE_ENTRY *entry;
71 for (entry = ccache_list; entry; entry = entry->next) {
77 void ccache_remove_all_after_fork(void)
79 struct WINBINDD_CCACHE_ENTRY *cur;
82 DLIST_REMOVE(ccache_list, cur);
83 TALLOC_FREE(cur->event);
89 static void krb5_ticket_gain_handler(struct event_context *event_ctx,
90 struct timed_event *te,
91 const struct timeval *now,
93 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
94 struct timed_event *te,
95 const struct timeval *now,
98 void ccache_regain_all_now(void)
100 struct WINBINDD_CCACHE_ENTRY *cur;
101 struct timeval t = timeval_current();
105 TALLOC_FREE(cur->event);
106 if (cur->refresh_time) {
107 cur->event = event_add_timed(winbind_event_context(),
109 "krb5_ticket_refresh_handler",
110 krb5_ticket_refresh_handler,
113 cur->event = event_add_timed(winbind_event_context(),
115 "krb5_ticket_gain_handler",
116 krb5_ticket_gain_handler,
120 DEBUG(0, ("ccache_regain_all_now: out of memory!!\n"));
126 /****************************************************************
127 The gain initial ticket is recognized as entry->refresh_time is
129 ****************************************************************/
131 static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry,
134 entry->refresh_time = 0;
135 entry->event = event_add_timed(winbind_event_context(), entry,
137 "krb5_ticket_gain_handler",
138 krb5_ticket_gain_handler,
142 /****************************************************************
143 Do the work of refreshing the ticket.
144 ****************************************************************/
146 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
147 struct timed_event *te,
148 const struct timeval *now,
151 struct WINBINDD_CCACHE_ENTRY *entry =
152 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
156 time_t expire_time = 0;
157 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
160 DEBUG(10,("krb5_ticket_refresh_handler called\n"));
161 DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
163 TALLOC_FREE(entry->event);
167 /* Kinit again if we have the user password and we can't renew the old
170 * This happens when machine are put to sleep for a very long time. */
172 if (entry->renew_until < time(NULL)) {
174 if (cred_ptr && cred_ptr->pass) {
176 set_effective_uid(entry->uid);
178 ret = kerberos_kinit_password_ext(entry->principal_name,
180 0, /* hm, can we do time correction here ? */
181 &entry->refresh_time,
184 False, /* no PAC required anymore */
186 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
187 gain_root_privilege();
190 DEBUG(3,("krb5_ticket_refresh_handler: "
191 "could not re-kinit: %s\n",
192 error_message(ret)));
193 /* destroy the ticket because we cannot rekinit
194 * it, ignore error here */
195 ads_kdestroy(entry->ccname);
197 /* Don't break the ticket refresh chain: retry
198 * refreshing ticket sometime later when KDC is
199 * unreachable -- BoYang.
200 * More error handling here? KRB5_CC_IO,
201 * KRB5KRB_AP_ERR_SKEW.
204 if ((ret == KRB5_KDC_UNREACH)
205 || (ret == KRB5_REALM_CANT_RESOLVE)) {
206 #if defined(DEBUG_KRB5_TKT_RENEWAL)
207 new_start = time(NULL) + 30;
209 new_start = time(NULL) +
210 MAX(30, lp_winbind_cache_time());
212 /* try to regain ticket here */
213 add_krb5_ticket_gain_handler_event(entry,
214 timeval_set(new_start, 0));
217 TALLOC_FREE(entry->event);
221 DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
222 "for: %s in ccache: %s\n",
223 entry->principal_name, entry->ccname));
225 #if defined(DEBUG_KRB5_TKT_RENEWAL)
226 new_start = time(NULL) + 30;
228 /* The tkt should be refreshed at one-half the period
229 from now to the expiration time */
230 expire_time = entry->refresh_time;
231 new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time);
236 * No cached credentials
237 * destroy ticket and refresh chain
239 ads_kdestroy(entry->ccname);
240 TALLOC_FREE(entry->event);
245 set_effective_uid(entry->uid);
247 ret = smb_krb5_renew_ticket(entry->ccname,
248 entry->principal_name,
251 #if defined(DEBUG_KRB5_TKT_RENEWAL)
252 new_start = time(NULL) + 30;
254 expire_time = new_start;
255 new_start = KRB5_EVENT_REFRESH_TIME(new_start);
258 gain_root_privilege();
261 DEBUG(3,("krb5_ticket_refresh_handler: could not renew tickets: %s\n",
262 error_message(ret)));
263 /* maybe we are beyond the renewing window */
265 /* evil rises here, we refresh ticket failed,
266 * but the ticket might be expired. Therefore,
267 * When we refresh ticket failed, destory the
270 ads_kdestroy(entry->ccname);
272 /* avoid breaking the renewal chain: retry in lp_winbind_cache_time()
273 * seconds when the KDC was not available right now.
274 * the return code can be KRB5_REALM_CANT_RESOLVE
275 * More error handling here? KRB5_CC_IO, KRB5KRB_AP_ERR_SKEW. */
277 if ((ret == KRB5_KDC_UNREACH)
278 || (ret == KRB5_REALM_CANT_RESOLVE)) {
279 #if defined(DEBUG_KRB5_TKT_RENEWAL)
280 new_start = time(NULL) + 30;
282 new_start = time(NULL) +
283 MAX(30, lp_winbind_cache_time());
285 /* ticket is destroyed here, we have to regain it
286 * if it is possible */
287 add_krb5_ticket_gain_handler_event(entry, timeval_set(new_start, 0));
290 /* This is evil, if the ticket was already expired.
291 * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED.
292 * But there is still a chance that we can rekinit it.
294 * This happens when user login in online mode, and then network
295 * down or something cause winbind goes offline for a very long time,
296 * and then goes online again. ticket expired, renew failed.
297 * This happens when machine are put to sleep for a long time,
298 * but shorter than entry->renew_util.
300 * Looks like the KDC is reachable, we want to rekinit as soon as
301 * possible instead of waiting some time later. */
302 if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED)
303 || (ret == KRB5_FCC_NOFILE)) goto rekinit;
310 /* in cases that ticket will be unrenewable soon, we don't try to renew ticket
311 * but try to regain ticket if it is possible */
312 if (entry->renew_until && expire_time
313 && (entry->renew_until <= expire_time)) {
314 /* try to regain ticket 10 seconds beforre expiration */
316 add_krb5_ticket_gain_handler_event(entry, timeval_set(expire_time, 0));
320 if (!entry->refresh_time) {
321 entry->refresh_time = new_start;
323 entry->event = event_add_timed(winbind_event_context(), entry,
324 timeval_set(new_start, 0),
325 "krb5_ticket_refresh_handler",
326 krb5_ticket_refresh_handler,
332 /****************************************************************
333 Do the work of regaining a ticket when coming from offline auth.
334 ****************************************************************/
336 static void krb5_ticket_gain_handler(struct event_context *event_ctx,
337 struct timed_event *te,
338 const struct timeval *now,
341 struct WINBINDD_CCACHE_ENTRY *entry =
342 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
346 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
347 struct winbindd_domain *domain = NULL;
350 DEBUG(10,("krb5_ticket_gain_handler called\n"));
351 DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
353 TALLOC_FREE(entry->event);
357 if (!cred_ptr || !cred_ptr->pass) {
358 DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
362 if ((domain = find_domain_from_name(entry->realm)) == NULL) {
363 DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
367 if (domain->online) {
369 set_effective_uid(entry->uid);
371 ret = kerberos_kinit_password_ext(entry->principal_name,
373 0, /* hm, can we do time correction here ? */
374 &entry->refresh_time,
377 False, /* no PAC required anymore */
379 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
380 gain_root_privilege();
383 DEBUG(3,("krb5_ticket_gain_handler: could not kinit: %s\n",
384 error_message(ret)));
385 /* evil. If we cannot do it, destroy any the __maybe__
386 * __existing__ ticket */
387 ads_kdestroy(entry->ccname);
391 DEBUG(10,("krb5_ticket_gain_handler: successful kinit for: %s in ccache: %s\n",
392 entry->principal_name, entry->ccname));
398 #if defined(DEBUG_KRB5_TKT_REGAIN)
399 t = timeval_set(time(NULL) + 30, 0);
401 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
403 add_krb5_ticket_gain_handler_event(entry, t);
409 #if defined(DEBUG_KRB5_TKT_RENEWAL)
410 t = timeval_set(time(NULL) + 30, 0);
412 t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0);
415 if (!entry->refresh_time) {
416 entry->refresh_time = t.tv_sec;
418 entry->event = event_add_timed(winbind_event_context(), entry,
420 "krb5_ticket_refresh_handler",
421 krb5_ticket_refresh_handler,
428 /****************************************************************
429 Check if an ccache entry exists.
430 ****************************************************************/
432 BOOL ccache_entry_exists(const char *username)
434 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
435 return (entry != NULL);
438 /****************************************************************
439 Ensure we're changing the correct entry.
440 ****************************************************************/
442 BOOL ccache_entry_identical(const char *username, uid_t uid, const char *ccname)
444 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
450 if (entry->uid != uid) {
451 DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
452 (unsigned int)entry->uid, (unsigned int)uid ));
455 if (!strcsequal(entry->ccname, ccname)) {
456 DEBUG(0,("cache_entry_identical: ccnames differ: (cache) %s != (client) %s\n",
457 entry->ccname, ccname));
463 NTSTATUS add_ccache_to_list(const char *princ_name,
466 const char *username,
472 BOOL postponed_request)
474 struct WINBINDD_CCACHE_ENTRY *entry = NULL;
480 if ((username == NULL && princ_name == NULL) || ccname == NULL || uid < 0) {
481 return NT_STATUS_INVALID_PARAMETER;
484 if (ccache_entry_count() + 1 > MAX_CCACHES) {
485 DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n"));
486 return NT_STATUS_NO_MORE_ENTRIES;
489 /* If it is cached login, destroy krb5 ticket
490 * to avoid surprise. */
492 if (postponed_request) {
493 /* ignore KRB5_FCC_NOFILE error here */
494 ret = ads_kdestroy(ccname);
495 if (ret == KRB5_FCC_NOFILE) {
499 DEBUG(0, ("add_ccache_to_list: failed to destroy "
500 "user krb5 ccache %s with %s\n", ccname,
501 error_message(ret)));
502 return krb5_to_nt_status(ret);
504 DEBUG(10, ("add_ccache_to_list: successfully destroyed "
505 "krb5 ccache %s for user %s\n", ccname,
511 /* Reference count old entries */
512 entry = get_ccache_by_username(username);
514 /* Check cached entries are identical. */
515 if (!ccache_entry_identical(username, uid, ccname)) {
516 return NT_STATUS_INVALID_PARAMETER;
519 DEBUG(10,("add_ccache_to_list: ref count on entry %s is now %d\n",
520 username, entry->ref_count));
521 /* FIXME: in this case we still might want to have a krb5 cred
522 * event handler created - gd
523 * Add ticket refresh handler here */
525 if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
531 if (postponed_request) {
532 t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
533 add_krb5_ticket_gain_handler_event(entry, t);
535 /* Renew at 1/2 the ticket expiration time */
536 #if defined(DEBUG_KRB5_TKT_RENEWAL)
537 t = timeval_set(time(NULL)+30, 0);
539 t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0);
541 if (!entry->refresh_time) {
542 entry->refresh_time = t.tv_sec;
544 entry->event = event_add_timed(winbind_event_context(),
547 "krb5_ticket_refresh_handler",
548 krb5_ticket_refresh_handler,
553 ntret = remove_ccache(username);
554 if (!NT_STATUS_IS_OK(ntret)) {
555 DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 "
556 "ccache %s for user %s\n", entry->ccname,
558 DEBUG(0, ("add_ccache_to_list: error is %s\n",
562 return NT_STATUS_NO_MEMORY;
565 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
571 entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY);
573 return NT_STATUS_NO_MEMORY;
579 entry->username = talloc_strdup(entry, username);
580 if (!entry->username) {
585 entry->principal_name = talloc_strdup(entry, princ_name);
586 if (!entry->principal_name) {
591 entry->service = talloc_strdup(entry, service);
592 if (!entry->service) {
597 entry->ccname = talloc_strdup(entry, ccname);
598 if (!entry->ccname) {
602 entry->realm = talloc_strdup(entry, realm);
607 entry->create_time = create_time;
608 entry->renew_until = renew_until;
610 entry->ref_count = 1;
612 if (lp_winbind_refresh_tickets() && renew_until > 0) {
614 if (postponed_request) {
615 add_krb5_ticket_gain_handler_event(entry, t);
617 /* Renew at 1/2 the ticket expiration time */
618 #if defined(DEBUG_KRB5_TKT_RENEWAL)
619 t = timeval_set(time(NULL)+30, 0);
621 t = timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0);
623 if (!entry->refresh_time) {
624 entry->refresh_time = t.tv_sec;
626 entry->event = event_add_timed(winbind_event_context(), entry,
628 "krb5_ticket_refresh_handler",
629 krb5_ticket_refresh_handler,
637 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
640 DLIST_ADD(ccache_list, entry);
642 DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username));
649 return NT_STATUS_NO_MEMORY;
652 /*******************************************************************
653 Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer referenced.
654 *******************************************************************/
656 NTSTATUS remove_ccache(const char *username)
658 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
659 NTSTATUS status = NT_STATUS_OK;
665 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
668 if (entry->ref_count <= 0) {
669 DEBUG(0,("remove_ccache: logic error. ref count for user %s = %d\n",
670 username, entry->ref_count));
671 return NT_STATUS_INTERNAL_DB_CORRUPTION;
676 if (entry->ref_count > 0) {
677 DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
678 username, entry->ref_count ));
682 /* no references any more */
684 DLIST_REMOVE(ccache_list, entry);
685 TALLOC_FREE(entry->event); /* unregisters events */
688 ret = ads_kdestroy(entry->ccname);
690 /* we ignore the error when there has been no credential cache */
691 if (ret == KRB5_FCC_NOFILE) {
694 DEBUG(0,("remove_ccache: failed to destroy user krb5 ccache %s with: %s\n",
695 entry->ccname, error_message(ret)));
697 DEBUG(10,("remove_ccache: successfully destroyed krb5 ccache %s for user %s\n",
698 entry->ccname, username));
700 status = krb5_to_nt_status(ret);
704 DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
709 /*******************************************************************
710 In memory credentials cache code.
711 *******************************************************************/
713 static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
715 /***********************************************************
716 Find an entry on the list by name.
717 ***********************************************************/
719 struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
721 struct WINBINDD_MEMORY_CREDS *p;
723 for (p = memory_creds_list; p; p = p->next) {
724 if (strequal(p->username, username)) {
731 /***********************************************************
732 Store the required creds and mlock them.
733 ***********************************************************/
735 static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, const char *pass)
737 #if !defined(HAVE_MLOCK)
740 /* new_entry->nt_hash is the base pointer for the block
741 of memory pointed into by new_entry->lm_hash and
742 new_entry->pass (if we're storing plaintext). */
744 memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
746 memcredp->len += strlen(pass)+1;
751 /* aligning the memory on on x86_64 and compiling
752 with gcc 4.1 using -O2 causes a segv in the
753 next memset() --jerry */
754 memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
756 /* On non-linux platforms, mlock()'d memory must be aligned */
757 memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char,
758 getpagesize(), memcredp->len);
760 if (!memcredp->nt_hash) {
761 return NT_STATUS_NO_MEMORY;
763 memset( memcredp->nt_hash, 0x0, memcredp->len );
765 memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
767 #ifdef DEBUG_PASSWORD
768 DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
770 if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
771 DEBUG(0,("failed to mlock memory: %s (%d)\n",
772 strerror(errno), errno));
773 SAFE_FREE(memcredp->nt_hash);
774 return map_nt_error_from_unix(errno);
777 #ifdef DEBUG_PASSWORD
778 DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
781 /* Create and store the password hashes. */
782 E_md4hash(pass, memcredp->nt_hash);
783 E_deshash(pass, memcredp->lm_hash);
786 memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
787 memcpy(memcredp->pass, pass, memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
794 /***********************************************************
795 Destroy existing creds.
796 ***********************************************************/
798 static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
800 #if !defined(HAVE_MUNLOCK)
803 if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
804 DEBUG(0,("failed to munlock memory: %s (%d)\n",
805 strerror(errno), errno));
806 return map_nt_error_from_unix(errno);
808 memset(memcredp->nt_hash, '\0', memcredp->len);
809 SAFE_FREE(memcredp->nt_hash);
810 memcredp->nt_hash = NULL;
811 memcredp->lm_hash = NULL;
812 memcredp->pass = NULL;
818 /***********************************************************
819 Replace the required creds with new ones (password change).
820 ***********************************************************/
822 static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
825 NTSTATUS status = delete_memory_creds(memcredp);
826 if (!NT_STATUS_IS_OK(status)) {
829 return store_memory_creds(memcredp, pass);
832 /*************************************************************
833 Store credentials in memory in a list.
834 *************************************************************/
836 static NTSTATUS winbindd_add_memory_creds_internal(const char *username, uid_t uid, const char *pass)
838 /* Shortcut to ensure we don't store if no mlock. */
839 #if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
843 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
845 if (uid == (uid_t)-1) {
846 DEBUG(0,("winbindd_add_memory_creds_internal: invalid uid for user %s.\n",
848 return NT_STATUS_INVALID_PARAMETER;
852 /* Already exists. Increment the reference count and replace stored creds. */
853 if (uid != memcredp->uid) {
854 DEBUG(0,("winbindd_add_memory_creds_internal: uid %u for user %s doesn't "
855 "match stored uid %u. Replacing.\n",
856 (unsigned int)uid, username, (unsigned int)memcredp->uid ));
859 memcredp->ref_count++;
860 DEBUG(10,("winbindd_add_memory_creds_internal: ref count for user %s is now %d\n",
861 username, memcredp->ref_count ));
862 return winbindd_replace_memory_creds_internal(memcredp, pass);
865 memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS);
867 return NT_STATUS_NO_MEMORY;
869 memcredp->username = talloc_strdup(memcredp, username);
870 if (!memcredp->username) {
871 talloc_destroy(memcredp);
872 return NT_STATUS_NO_MEMORY;
875 status = store_memory_creds(memcredp, pass);
876 if (!NT_STATUS_IS_OK(status)) {
877 talloc_destroy(memcredp);
882 memcredp->ref_count = 1;
883 DLIST_ADD(memory_creds_list, memcredp);
885 DEBUG(10,("winbindd_add_memory_creds_internal: added entry for user %s\n",
892 /*************************************************************
893 Store users credentials in memory. If we also have a
894 struct WINBINDD_CCACHE_ENTRY for this username with a
895 refresh timer, then store the plaintext of the password
896 and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY.
897 *************************************************************/
899 NTSTATUS winbindd_add_memory_creds(const char *username, uid_t uid, const char *pass)
901 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
904 status = winbindd_add_memory_creds_internal(username, uid, pass);
905 if (!NT_STATUS_IS_OK(status)) {
910 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
912 entry->cred_ptr = memcredp;
919 /*************************************************************
920 Decrement the in-memory ref count - delete if zero.
921 *************************************************************/
923 NTSTATUS winbindd_delete_memory_creds(const char *username)
925 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
926 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
927 NTSTATUS status = NT_STATUS_OK;
930 DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
932 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
935 if (memcredp->ref_count <= 0) {
936 DEBUG(0,("winbindd_delete_memory_creds: logic error. ref count for user %s = %d\n",
937 username, memcredp->ref_count));
938 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
941 memcredp->ref_count--;
942 if (memcredp->ref_count <= 0) {
943 delete_memory_creds(memcredp);
944 DLIST_REMOVE(memory_creds_list, memcredp);
945 talloc_destroy(memcredp);
946 DEBUG(10,("winbindd_delete_memory_creds: deleted entry for user %s\n",
949 DEBUG(10,("winbindd_delete_memory_creds: entry for user %s ref_count now %d\n",
950 username, memcredp->ref_count));
954 entry->cred_ptr = NULL; /* Ensure we have no dangling references to this. */
959 /***********************************************************
960 Replace the required creds with new ones (password change).
961 ***********************************************************/
963 NTSTATUS winbindd_replace_memory_creds(const char *username, const char *pass)
965 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
968 DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
970 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
973 DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
976 return winbindd_replace_memory_creds_internal(memcredp, pass);