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