Fix possible uninitialized variable use.
[samba.git] / source / 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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
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
33 #endif
34
35 #define MAX_CCACHES 100
36
37 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
38
39 /* The Krb5 ticket refresh handler should be scheduled 
40    at one-half of the period from now till the tkt 
41    expiration */
42 #define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2))
43
44 /****************************************************************
45  Find an entry by name.
46 ****************************************************************/
47
48 static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
49 {
50         struct WINBINDD_CCACHE_ENTRY *entry;
51
52         for (entry = ccache_list; entry; entry = entry->next) {
53                 if (strequal(entry->username, username)) {
54                         return entry;
55                 }
56         }
57         return NULL;
58 }
59
60 /****************************************************************
61  How many do we have ?
62 ****************************************************************/
63
64 static int ccache_entry_count(void)
65 {
66         struct WINBINDD_CCACHE_ENTRY *entry;
67         int i = 0;
68
69         for (entry = ccache_list; entry; entry = entry->next) {
70                 i++;
71         }
72         return i;
73 }
74
75 /****************************************************************
76  Do the work of refreshing the ticket.
77 ****************************************************************/
78
79 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
80                                         struct timed_event *te,
81                                         const struct timeval *now,
82                                         void *private_data)
83 {
84         struct WINBINDD_CCACHE_ENTRY *entry =
85                 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
86 #ifdef HAVE_KRB5
87         int ret;
88         time_t new_start;
89         struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
90 #endif
91
92         DEBUG(10,("krb5_ticket_refresh_handler called\n"));
93         DEBUGADD(10,("event called for: %s, %s\n", 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                 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                 gain_root_privilege();
229
230                 if (ret) {
231                         DEBUG(3,("krb5_ticket_gain_handler: could not kinit: %s\n",
232                                 error_message(ret)));
233                         goto retry_later;
234                 }
235
236                 DEBUG(10,("krb5_ticket_gain_handler: successful kinit for: %s in ccache: %s\n",
237                         entry->principal_name, entry->ccname));
238
239                 goto got_ticket;
240         }
241
242   retry_later:
243
244         entry->event = event_add_timed(winbind_event_context(), entry,
245                                         timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0),
246                                         "krb5_ticket_gain_handler",
247                                         krb5_ticket_gain_handler,
248                                         entry);
249
250         return;
251
252   got_ticket:
253
254 #if defined(DEBUG_KRB5_TKT_RENEWAL)
255         t = timeval_set(time(NULL) + 30, 0);
256 #else
257         t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0);
258 #endif
259
260         entry->event = event_add_timed(winbind_event_context(), entry,
261                                         t,
262                                         "krb5_ticket_refresh_handler",
263                                         krb5_ticket_refresh_handler,
264                                         entry);
265
266         return;
267 #endif
268 }
269
270 /****************************************************************
271  Check if an ccache entry exists.
272 ****************************************************************/
273
274 BOOL ccache_entry_exists(const char *username)
275 {
276         struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
277         return (entry != NULL);
278 }
279
280 /****************************************************************
281  Ensure we're changing the correct entry.
282 ****************************************************************/
283
284 BOOL ccache_entry_identical(const char *username, uid_t uid, const char *ccname)
285 {
286         struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
287
288         if (!entry) {
289                 return False;
290         }
291
292         if (entry->uid != uid) {
293                 DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
294                         (unsigned int)entry->uid, (unsigned int)uid ));
295                 return False;
296         }
297         if (!strcsequal(entry->ccname, ccname)) {
298                 DEBUG(0,("cache_entry_identical: ccnames differ: (cache) %s != (client) %s\n",
299                         entry->ccname, ccname));
300                 return False;
301         }
302         return True;
303 }
304
305 NTSTATUS add_ccache_to_list(const char *princ_name,
306                             const char *ccname,
307                             const char *service,
308                             const char *username, 
309                             const char *realm,
310                             uid_t uid,
311                             time_t create_time, 
312                             time_t ticket_end, 
313                             time_t renew_until, 
314                             BOOL postponed_request)
315 {
316         struct WINBINDD_CCACHE_ENTRY *entry = NULL;
317
318         if ((username == NULL && princ_name == NULL) || ccname == NULL || uid < 0) {
319                 return NT_STATUS_INVALID_PARAMETER;
320         }
321
322         if (ccache_entry_count() + 1 > MAX_CCACHES) {
323                 DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n"));
324                 return NT_STATUS_NO_MORE_ENTRIES;
325         }
326
327         /* Reference count old entries */
328         entry = get_ccache_by_username(username);
329         if (entry) {
330                 /* Check cached entries are identical. */
331                 if (!ccache_entry_identical(username, uid, ccname)) {
332                         return NT_STATUS_INVALID_PARAMETER;
333                 }
334                 entry->ref_count++;
335                 DEBUG(10,("add_ccache_to_list: ref count on entry %s is now %d\n",
336                         username, entry->ref_count));
337                 /* FIXME: in this case we still might want to have a krb5 cred
338                  * event handler created - gd*/
339                 return NT_STATUS_OK;
340         }
341         
342         entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY);
343         if (!entry) {
344                 return NT_STATUS_NO_MEMORY;
345         }
346
347         ZERO_STRUCTP(entry);
348
349         if (username) {
350                 entry->username = talloc_strdup(entry, username);
351                 if (!entry->username) {
352                         goto no_mem;
353                 }
354         }
355         if (princ_name) {
356                 entry->principal_name = talloc_strdup(entry, princ_name);
357                 if (!entry->principal_name) {
358                         goto no_mem;
359                 }
360         }
361         if (service) {
362                 entry->service = talloc_strdup(entry, service);
363                 if (!entry->service) {
364                         goto no_mem;
365                 }
366         }
367
368         entry->ccname = talloc_strdup(entry, ccname);
369         if (!entry->ccname) {
370                 goto no_mem;
371         }
372
373         entry->realm = talloc_strdup(entry, realm);
374         if (!entry->realm) {
375                 goto no_mem;
376         }
377
378         entry->create_time = create_time;
379         entry->renew_until = renew_until;
380         entry->uid = uid;
381         entry->ref_count = 1;
382
383         if (lp_winbind_refresh_tickets() && renew_until > 0) {
384                 if (postponed_request) {
385                         entry->event = event_add_timed(winbind_event_context(), entry,
386                                                 timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0),
387                                                 "krb5_ticket_gain_handler",
388                                                 krb5_ticket_gain_handler,
389                                                 entry);
390                 } else {
391                         /* Renew at 1/2 the ticket expiration time */
392                         entry->event = event_add_timed(winbind_event_context(), entry,
393 #if defined(DEBUG_KRB5_TKT_RENEWAL)
394                                                 timeval_set(time(NULL)+30, 0),
395 #else
396                                                 timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0),
397 #endif
398                                                 "krb5_ticket_refresh_handler",
399                                                 krb5_ticket_refresh_handler,
400                                                 entry);
401                 }
402
403                 if (!entry->event) {
404                         goto no_mem;
405                 }
406
407                 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
408         }
409
410         DLIST_ADD(ccache_list, entry);
411
412         DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username));
413
414         return NT_STATUS_OK;
415
416  no_mem:
417
418         TALLOC_FREE(entry);
419         return NT_STATUS_NO_MEMORY;
420 }
421
422 /*******************************************************************
423  Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer referenced.
424 *******************************************************************/
425
426 NTSTATUS remove_ccache(const char *username)
427 {
428         struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
429         NTSTATUS status = NT_STATUS_OK;
430 #ifdef HAVE_KRB5
431         krb5_error_code ret;
432 #endif
433
434         if (!entry) {
435                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
436         }
437
438         if (entry->ref_count <= 0) {
439                 DEBUG(0,("remove_ccache: logic error. ref count for user %s = %d\n",
440                         username, entry->ref_count));
441                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
442         }
443
444         entry->ref_count--;
445
446         if (entry->ref_count > 0) {
447                 DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
448                         username, entry->ref_count ));
449                 return NT_STATUS_OK;
450         }
451
452         /* no references any more */
453
454         DLIST_REMOVE(ccache_list, entry);
455         TALLOC_FREE(entry->event); /* unregisters events */
456
457 #ifdef HAVE_KRB5
458         ret = ads_kdestroy(entry->ccname);
459
460         /* we ignore the error when there has been no credential cache */
461         if (ret == KRB5_FCC_NOFILE) {
462                 ret = 0;
463         } else if (ret) {
464                 DEBUG(0,("remove_ccache: failed to destroy user krb5 ccache %s with: %s\n",
465                         entry->ccname, error_message(ret)));
466         } else {
467                 DEBUG(10,("remove_ccache: successfully destroyed krb5 ccache %s for user %s\n",
468                         entry->ccname, username));
469         }
470         status = krb5_to_nt_status(ret);
471 #endif
472
473         TALLOC_FREE(entry);
474         DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
475
476         return status;
477 }
478
479 /*******************************************************************
480  In memory credentials cache code.
481 *******************************************************************/
482
483 static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
484
485 /***********************************************************
486  Find an entry on the list by name.
487 ***********************************************************/
488
489 struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
490 {
491         struct WINBINDD_MEMORY_CREDS *p;
492
493         for (p = memory_creds_list; p; p = p->next) {
494                 if (strequal(p->username, username)) {
495                         return p;
496                 }
497         }
498         return NULL;
499 }
500
501 /***********************************************************
502  Store the required creds and mlock them.
503 ***********************************************************/
504
505 static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, const char *pass)
506 {
507 #if !defined(HAVE_MLOCK)
508         return NT_STATUS_OK;
509 #else
510         /* new_entry->nt_hash is the base pointer for the block
511            of memory pointed into by new_entry->lm_hash and
512            new_entry->pass (if we're storing plaintext). */
513
514         memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
515         if (pass) {
516                 memcredp->len += strlen(pass)+1;
517         }
518
519
520 #if defined(LINUX)
521         /* aligning the memory on on x86_64 and compiling 
522            with gcc 4.1 using -O2 causes a segv in the 
523            next memset()  --jerry */
524         memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
525 #else
526         /* On non-linux platforms, mlock()'d memory must be aligned */
527         memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char, 
528                                                getpagesize(), memcredp->len);
529 #endif
530         if (!memcredp->nt_hash) {
531                 return NT_STATUS_NO_MEMORY;
532         }
533         memset( memcredp->nt_hash, 0x0, memcredp->len );
534
535         memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
536
537 #ifdef DEBUG_PASSWORD
538         DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
539 #endif          
540         if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
541                 DEBUG(0,("failed to mlock memory: %s (%d)\n", 
542                         strerror(errno), errno));
543                 SAFE_FREE(memcredp->nt_hash);
544                 return map_nt_error_from_unix(errno);
545         }
546
547 #ifdef DEBUG_PASSWORD
548         DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
549 #endif          
550
551         /* Create and store the password hashes. */
552         E_md4hash(pass, memcredp->nt_hash);
553         E_deshash(pass, memcredp->lm_hash);
554
555         if (pass) {
556                 memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
557                 memcpy(memcredp->pass, pass, memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
558         }
559
560         return NT_STATUS_OK;
561 #endif
562 }
563
564 /***********************************************************
565  Destroy existing creds.
566 ***********************************************************/
567
568 static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
569 {
570 #if !defined(HAVE_MUNLOCK)
571         return NT_STATUS_OK;
572 #else
573         if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
574                 DEBUG(0,("failed to munlock memory: %s (%d)\n", 
575                         strerror(errno), errno));
576                 return map_nt_error_from_unix(errno);
577         }
578         memset(memcredp->nt_hash, '\0', memcredp->len);
579         SAFE_FREE(memcredp->nt_hash);
580         memcredp->nt_hash = NULL;
581         memcredp->lm_hash = NULL;
582         memcredp->pass = NULL;
583         memcredp->len = 0;
584         return NT_STATUS_OK;
585 #endif
586 }
587
588 /***********************************************************
589  Replace the required creds with new ones (password change).
590 ***********************************************************/
591
592 static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
593                                                 const char *pass)
594 {
595         NTSTATUS status = delete_memory_creds(memcredp);
596         if (!NT_STATUS_IS_OK(status)) {
597                 return status;
598         }
599         return store_memory_creds(memcredp, pass);
600 }
601
602 /*************************************************************
603  Store credentials in memory in a list.
604 *************************************************************/
605
606 static NTSTATUS winbindd_add_memory_creds_internal(const char *username, uid_t uid, const char *pass)
607 {
608         /* Shortcut to ensure we don't store if no mlock. */
609 #if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
610         return NT_STATUS_OK;
611 #else
612         NTSTATUS status;
613         struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
614
615         if (uid == (uid_t)-1) {
616                 DEBUG(0,("winbindd_add_memory_creds_internal: invalid uid for user %s.\n",
617                         username ));
618                 return NT_STATUS_INVALID_PARAMETER;
619         }
620
621         if (memcredp) {
622                 /* Already exists. Increment the reference count and replace stored creds. */
623                 if (uid != memcredp->uid) {
624                         DEBUG(0,("winbindd_add_memory_creds_internal: uid %u for user %s doesn't "
625                                 "match stored uid %u. Replacing.\n",
626                                 (unsigned int)uid, username, (unsigned int)memcredp->uid ));
627                         memcredp->uid = uid;
628                 }
629                 memcredp->ref_count++;
630                 DEBUG(10,("winbindd_add_memory_creds_internal: ref count for user %s is now %d\n",
631                         username, memcredp->ref_count ));
632                 return winbindd_replace_memory_creds_internal(memcredp, pass);
633         }
634
635         memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS);
636         if (!memcredp) {
637                 return NT_STATUS_NO_MEMORY;
638         }
639         memcredp->username = talloc_strdup(memcredp, username);
640         if (!memcredp->username) {
641                 talloc_destroy(memcredp);
642                 return NT_STATUS_NO_MEMORY;
643         }
644
645         status = store_memory_creds(memcredp, pass);
646         if (!NT_STATUS_IS_OK(status)) {
647                 talloc_destroy(memcredp);
648                 return status;
649         }
650
651         memcredp->uid = uid;
652         memcredp->ref_count = 1;
653         DLIST_ADD(memory_creds_list, memcredp);
654
655         DEBUG(10,("winbindd_add_memory_creds_internal: added entry for user %s\n",
656                 username ));
657
658         return NT_STATUS_OK;
659 #endif
660 }
661
662 /*************************************************************
663  Store users credentials in memory. If we also have a 
664  struct WINBINDD_CCACHE_ENTRY for this username with a
665  refresh timer, then store the plaintext of the password
666  and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY.
667 *************************************************************/
668
669 NTSTATUS winbindd_add_memory_creds(const char *username, uid_t uid, const char *pass)
670 {
671         struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
672         NTSTATUS status;
673
674         status = winbindd_add_memory_creds_internal(username, uid, pass);
675         if (!NT_STATUS_IS_OK(status)) {
676                 return status;
677         }
678
679         if (entry) {
680                 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
681                 if (memcredp) {
682                         entry->cred_ptr = memcredp;
683                 }
684         }
685
686         return status;
687 }
688
689 /*************************************************************
690  Decrement the in-memory ref count - delete if zero.
691 *************************************************************/
692
693 NTSTATUS winbindd_delete_memory_creds(const char *username)
694 {
695         struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
696         struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
697         NTSTATUS status = NT_STATUS_OK;
698
699         if (!memcredp) {
700                 DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
701                         username ));
702                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
703         }
704
705         if (memcredp->ref_count <= 0) {
706                 DEBUG(0,("winbindd_delete_memory_creds: logic error. ref count for user %s = %d\n",
707                         username, memcredp->ref_count));
708                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
709         }
710
711         memcredp->ref_count--;
712         if (memcredp->ref_count <= 0) {
713                 delete_memory_creds(memcredp);
714                 DLIST_REMOVE(memory_creds_list, memcredp);
715                 talloc_destroy(memcredp);
716                 DEBUG(10,("winbindd_delete_memory_creds: deleted entry for user %s\n",
717                         username));
718         } else {
719                 DEBUG(10,("winbindd_delete_memory_creds: entry for user %s ref_count now %d\n",
720                         username, memcredp->ref_count));
721         }
722
723         if (entry) {
724                 entry->cred_ptr = NULL; /* Ensure we have no dangling references to this. */
725         }
726         return status;
727 }
728
729 /***********************************************************
730  Replace the required creds with new ones (password change).
731 ***********************************************************/
732
733 NTSTATUS winbindd_replace_memory_creds(const char *username, const char *pass)
734 {
735         struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
736
737         if (!memcredp) {
738                 DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
739                         username ));
740                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
741         }
742
743         DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
744                 username ));
745
746         return winbindd_replace_memory_creds_internal(memcredp, pass);
747 }