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