diff options
author | Jeremy Allison <jra@samba.org> | 2006-08-26 02:53:45 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:38:53 -0500 |
commit | e627362622344f70cd12ded917fe9d82feb63f1a (patch) | |
tree | fe1030ad60424820b76b65d8a4c619f6b334326d | |
parent | 47e29ebe0975f63f972d137fca30fec843492f78 (diff) | |
download | samba-e627362622344f70cd12ded917fe9d82feb63f1a.tar.gz samba-e627362622344f70cd12ded917fe9d82feb63f1a.tar.bz2 samba-e627362622344f70cd12ded917fe9d82feb63f1a.zip |
r17837: Split out the storing of memory cached credentials
from the krb5 ticket renewal code. This allows cached
credentials to be stored for single sign-on via ntlm_auth
for machines in a domain still using NTLM. Also (hopefully)
fixes the reference counting problem with pam_logon/logoff
so multiple logons/logoffs won't lose cached credentials.
This compiles, but I'm intending to test it over the weekend
so don't complain too much :-). I also want it in the tree
so Coverity can scan it for errors. Guenther, check this over
please - I ran through the architecture with Jerry and he's
ok with it, but this is modifying your code a lot.
Jeremy.
(This used to be commit 679eeeb91155dad3942efde6ae9f8d81faf18c5b)
-rw-r--r-- | source3/nsswitch/winbindd_ccache_access.c | 4 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cred_cache.c | 491 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_nss.h | 17 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 111 |
4 files changed, 422 insertions, 201 deletions
diff --git a/source3/nsswitch/winbindd_ccache_access.c b/source3/nsswitch/winbindd_ccache_access.c index d17f15ae9f..6aa221448e 100644 --- a/source3/nsswitch/winbindd_ccache_access.c +++ b/source3/nsswitch/winbindd_ccache_access.c @@ -230,7 +230,7 @@ enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *doma } entry = get_ccache_by_username(state->request.data.ccache_ntlm_auth.user); - if (entry == NULL) { + if (entry == NULL || entry->cred_ptr == NULL) { DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find " "credentials for user %s\n", state->request.data.ccache_ntlm_auth.user)); @@ -258,7 +258,7 @@ enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *doma result = NT_STATUS_NO_MEMORY; } else { result = do_ntlm_auth_with_hashes(name_user, name_domain, - entry->lm_hash, entry->nt_hash, + entry->cred_ptr->lm_hash, entry->cred_ptr->nt_hash, initial, challenge, &auth); } diff --git a/source3/nsswitch/winbindd_cred_cache.c b/source3/nsswitch/winbindd_cred_cache.c index b56003a469..ceca389825 100644 --- a/source3/nsswitch/winbindd_cred_cache.c +++ b/source3/nsswitch/winbindd_cred_cache.c @@ -2,8 +2,10 @@ Unix SMB/CIFS implementation. Winbind daemon - krb5 credential cache funcions + and in-memory cache functions. Copyright (C) Guenther Deschner 2005 + Copyright (C) Jeremy Allison 2006 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,23 +27,13 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND -#define MAX_CCACHES 10000 +#define MAX_CCACHES 100 static struct WINBINDD_CCACHE_ENTRY *ccache_list; -static TALLOC_CTX *mem_ctx; - -const char *get_ccache_name_by_username(const char *username) -{ - struct WINBINDD_CCACHE_ENTRY *entry; - - for (entry = ccache_list; entry; entry = entry->next) { - if (strequal(entry->username, username)) { - return entry->ccname; - } - } - return NULL; -} +/**************************************************************** + Find an entry by name. +****************************************************************/ struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username) { @@ -55,6 +47,10 @@ struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username) return NULL; } +/**************************************************************** + How many do we have ? +****************************************************************/ + static int ccache_entry_count(void) { struct WINBINDD_CCACHE_ENTRY *entry; @@ -66,43 +62,9 @@ static int ccache_entry_count(void) return i; } -NTSTATUS remove_ccache_by_ccname(const char *ccname) -{ - struct WINBINDD_CCACHE_ENTRY *entry; - - for (entry = ccache_list; entry; entry = entry->next) { - if (strequal(entry->ccname, ccname)) { - DLIST_REMOVE(ccache_list, entry); - TALLOC_FREE(entry->event); /* unregisters events */ -#ifdef HAVE_MUNLOCK - if (entry->nt_hash) { - size_t len = NT_HASH_LEN + LM_HASH_LEN; - - if (entry->pass) { - len += strlen(entry->pass)+1; - } - -#ifdef DEBUG_PASSWORD - DEBUG(10,("unlocking memory: %p\n", entry->nt_hash)); -#endif - memset(entry->nt_hash, 0, len); - if ((munlock(entry->nt_hash, len)) == -1) { - DEBUG(0,("failed to munlock memory: %s (%d)\n", - strerror(errno), errno)); - return map_nt_error_from_unix(errno); - } -#ifdef DEBUG_PASSWORD - DEBUG(10,("munlocked memory: %p\n", entry->nt_hash)); -#endif - } -#endif /* HAVE_MUNLOCK */ - TALLOC_FREE(entry); - DEBUG(10,("remove_ccache_by_ccname: removed ccache %s\n", ccname)); - return NT_STATUS_OK; - } - } - return NT_STATUS_OBJECT_NAME_NOT_FOUND; -} +/**************************************************************** + Do the work of refreshing the ticket. +****************************************************************/ static void krb5_ticket_refresh_handler(struct timed_event *te, const struct timeval *now, @@ -113,6 +75,7 @@ static void krb5_ticket_refresh_handler(struct timed_event *te, int ret; time_t new_start; struct timeval t; + struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; DEBUG(10,("krb5_ticket_refresh_handler called\n")); DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username)); @@ -124,12 +87,12 @@ static void krb5_ticket_refresh_handler(struct timed_event *te, /* Kinit again if we have the user password and we can't renew the old * tgt anymore */ - if ((entry->renew_until < time(NULL)) && (entry->pass != NULL)) { + if ((entry->renew_until < time(NULL)) && cred_ptr && cred_ptr->pass) { set_effective_uid(entry->uid); ret = kerberos_kinit_password_ext(entry->principal_name, - entry->pass, + cred_ptr->pass, 0, /* hm, can we do time correction here ? */ &entry->refresh_time, &entry->renew_until, @@ -171,7 +134,7 @@ done: t = timeval_set(new_start, 0); - entry->event = add_timed_event(mem_ctx, + entry->event = add_timed_event(entry, t, "krb5_ticket_refresh_handler", krb5_ticket_refresh_handler, @@ -180,6 +143,31 @@ done: #endif } +/**************************************************************** + Ensure we're changing the correct entry. +****************************************************************/ + +BOOL ccache_entry_identical(const char *username, uid_t uid, const char *ccname) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + + if (!entry) { + return False; + } + + if (entry->uid != uid) { + DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n", + (unsigned int)entry->uid, (unsigned int)uid )); + return False; + } + if (!strcsequal(entry->ccname, ccname)) { + DEBUG(0,("cache_entry_identical: ccnames differ: (cache) %s != (client) %s\n", + entry->ccname, ccname)); + return False; + } + return True; +} + NTSTATUS add_ccache_to_list(const char *princ_name, const char *ccname, const char *service, @@ -192,156 +180,367 @@ NTSTATUS add_ccache_to_list(const char *princ_name, time_t renew_until, BOOL schedule_refresh_event) { - struct WINBINDD_CCACHE_ENTRY *new_entry = NULL; - struct WINBINDD_CCACHE_ENTRY *old_entry = NULL; - NTSTATUS status; + struct WINBINDD_CCACHE_ENTRY *entry = NULL; if ((username == NULL && sid_string == NULL && princ_name == NULL) || - ccname == NULL) { + ccname == NULL || uid < 0) { return NT_STATUS_INVALID_PARAMETER; } - status = init_ccache_list(); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (mem_ctx == NULL) { - return NT_STATUS_NO_MEMORY; - } - if (ccache_entry_count() + 1 > MAX_CCACHES) { DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n")); return NT_STATUS_NO_MORE_ENTRIES; } - /* get rid of old entries */ - old_entry = get_ccache_by_username(username); - if (old_entry) { - status = remove_ccache_by_ccname(old_entry->ccname); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("add_ccache_to_list: failed to delete old ccache entry\n")); - return status; + /* Reference count old entries */ + entry = get_ccache_by_username(username); + if (entry) { + /* Check cached entries are identical. */ + if (!ccache_entry_identical(username, uid, ccname)) { + return NT_STATUS_INVALID_PARAMETER; } + entry->ref_count++; + DEBUG(10,("add_ccache_to_list: ref count on entry %s is now %d\n", + username, entry->ref_count)); + return NT_STATUS_OK; } - new_entry = TALLOC_P(mem_ctx, struct WINBINDD_CCACHE_ENTRY); - if (new_entry == NULL) { + entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY); + if (!entry) { return NT_STATUS_NO_MEMORY; } - ZERO_STRUCTP(new_entry); + ZERO_STRUCTP(entry); if (username) { - new_entry->username = talloc_strdup(mem_ctx, username); - NT_STATUS_HAVE_NO_MEMORY(new_entry->username); + entry->username = talloc_strdup(entry, username); + if (!entry->username) { + goto no_mem; + } } if (sid_string) { - new_entry->sid_string = talloc_strdup(mem_ctx, sid_string); - NT_STATUS_HAVE_NO_MEMORY(new_entry->sid_string); + entry->sid_string = talloc_strdup(entry, sid_string); + if (!entry->sid_string) { + goto no_mem; + } } if (princ_name) { - new_entry->principal_name = talloc_strdup(mem_ctx, princ_name); - NT_STATUS_HAVE_NO_MEMORY(new_entry->principal_name); + entry->principal_name = talloc_strdup(entry, princ_name); + if (!entry->principal_name) { + goto no_mem; + } } if (service) { - new_entry->service = talloc_strdup(mem_ctx, service); - NT_STATUS_HAVE_NO_MEMORY(new_entry->service); + entry->service = talloc_strdup(entry, service); + if (!entry->service) { + goto no_mem; + } } - if (pass) { - size_t len = NT_HASH_LEN + LM_HASH_LEN; + entry->ccname = talloc_strdup(entry, ccname); + if (!entry->ccname) { + goto no_mem; + } - /* We only store the plaintext if we're going to - schedule a krb5 refresh. */ + entry->create_time = create_time; + entry->renew_until = renew_until; + entry->uid = uid; - if (schedule_refresh_event) { - len += strlen(pass)+1; - } - - /* new_entry->nt_hash is the base pointer for the block - of memory pointed into by new_entry->lm_hash and - new_entry->pass (if we're storing plaintext). */ + if (schedule_refresh_event && renew_until > 0) { - new_entry->nt_hash = (unsigned char *)TALLOC_ZERO(mem_ctx, len); - NT_STATUS_HAVE_NO_MEMORY(new_entry->nt_hash); + struct timeval t = timeval_set((ticket_end -1 ), 0); - new_entry->lm_hash = new_entry->nt_hash + NT_HASH_LEN; -#ifdef HAVE_MLOCK -#ifdef DEBUG_PASSWORD - DEBUG(10,("mlocking memory: %p\n", new_entry->nt_hash)); -#endif - if ((mlock(new_entry->nt_hash, len)) == -1) { - DEBUG(0,("failed to mlock memory: %s (%d)\n", - strerror(errno), errno)); - return map_nt_error_from_unix(errno); - } - -#ifdef DEBUG_PASSWORD - DEBUG(10,("mlocked memory: %p\n", new_entry->nt_hash)); -#endif -#endif /* HAVE_MLOCK */ + entry->event = add_timed_event(entry, + t, + "krb5_ticket_refresh_handler", + krb5_ticket_refresh_handler, + entry); + } + + DLIST_ADD(ccache_list, entry); + + DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username)); - /* Create and store the password hashes. */ - E_md4hash(pass, new_entry->nt_hash); - E_deshash(pass, new_entry->lm_hash); + return NT_STATUS_OK; + + no_mem: + + TALLOC_FREE(entry); + return NT_STATUS_NO_MEMORY; +} + +NTSTATUS remove_ccache(const char *username) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + + if (!entry) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (entry->ref_count <= 0) { + DEBUG(0,("remove_ccache: logic error. ref count for user %s = %d\n", + username, entry->ref_count)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + entry->ref_count--; + if (entry->ref_count <= 0) { + DLIST_REMOVE(ccache_list, entry); + TALLOC_FREE(entry->event); /* unregisters events */ + TALLOC_FREE(entry); + DEBUG(10,("remove_ccache: removed ccache for user %s\n", username)); + } else { + DEBUG(10,("remove_ccache: entry %s ref count now %d\n", + username, entry->ref_count )); + } + + return NT_STATUS_OK; +} + +/******************************************************************* + In memory credentials cache code. +*******************************************************************/ + +static struct WINBINDD_MEMORY_CREDS *memory_creds_list; + +/*********************************************************** + Find an entry on the list by name. +***********************************************************/ + +static struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username) +{ + struct WINBINDD_MEMORY_CREDS *p; - if (schedule_refresh_event) { - new_entry->pass = (char *)new_entry->lm_hash + LM_HASH_LEN; - memcpy(new_entry->pass, pass, len - NT_HASH_LEN - LM_HASH_LEN); + for (p = memory_creds_list; p; p = p->next) { + if (strequal(p->username, username)) { + return p; } } + return NULL; +} - new_entry->create_time = create_time; - new_entry->renew_until = renew_until; - new_entry->ccname = talloc_strdup(mem_ctx, ccname); - if (new_entry->ccname == NULL) { - return NT_STATUS_NO_MEMORY; +/*********************************************************** + Store the required creds and mlock them. +***********************************************************/ + +static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, const char *pass, BOOL store_pass) +{ +#if !defined(HAVE_MLOCK) + return NT_STATUS_OK; +#else + /* new_entry->nt_hash is the base pointer for the block + of memory pointed into by new_entry->lm_hash and + new_entry->pass (if we're storing plaintext). */ + + memcredp->len = NT_HASH_LEN + LM_HASH_LEN; + if (pass && store_pass) { + memcredp->len += strlen(pass)+1; } - new_entry->uid = uid; - if (schedule_refresh_event && renew_until > 0) { + memcredp->nt_hash = (unsigned char *)TALLOC_ZERO(memcredp, memcredp->len); + if (!memcredp->nt_hash) { + return NT_STATUS_NO_MEMORY; + } - struct timeval t = timeval_set((ticket_end -1 ), 0); + memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN; +#ifdef DEBUG_PASSWORD + DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash)); +#endif - new_entry->event = add_timed_event(mem_ctx, - t, - "krb5_ticket_refresh_handler", - krb5_ticket_refresh_handler, - new_entry); + if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) { + DEBUG(0,("failed to mlock memory: %s (%d)\n", + strerror(errno), errno)); + return map_nt_error_from_unix(errno); } - DLIST_ADD(ccache_list, new_entry); +#ifdef DEBUG_PASSWORD + DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash)); +#endif - DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username)); + /* Create and store the password hashes. */ + E_md4hash(pass, memcredp->nt_hash); + E_deshash(pass, memcredp->lm_hash); + + if (pass && store_pass) { + memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN; + memcpy(memcredp->pass, pass, memcredp->len - NT_HASH_LEN - LM_HASH_LEN); + } return NT_STATUS_OK; +#endif } -NTSTATUS destroy_ccache_list(void) +/*********************************************************** + Destroy existing creds. +***********************************************************/ + +static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp) { -#ifdef HAVE_MUNLOCKALL - if ((munlockall()) == -1) { - DEBUG(0,("failed to unlock memory: %s (%d)\n", +#if !defined(HAVE_MUNLOCK) + return NT_STATUS_OK; +#else + if (munlock(memcredp->nt_hash, memcredp->len) == -1) { + DEBUG(0,("failed to munlock memory: %s (%d)\n", strerror(errno), errno)); return map_nt_error_from_unix(errno); } -#endif /* HAVE_MUNLOCKALL */ - return talloc_destroy(mem_ctx) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; + memset(memcredp->nt_hash, '\0', memcredp->len); + TALLOC_FREE(memcredp->nt_hash); + memcredp->lm_hash = NULL; + memcredp->pass = NULL; + memcredp->len = 0; + return NT_STATUS_OK; +#endif } -NTSTATUS init_ccache_list(void) +/*********************************************************** + Replace the required creds with new ones (password change). +***********************************************************/ + +static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp, + const char *pass, BOOL store_pass) { - if (ccache_list) { - return NT_STATUS_OK; + NTSTATUS status = delete_memory_creds(memcredp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return store_memory_creds(memcredp, pass, store_pass); +} + +/************************************************************* + Store credentials in memory in a list. +*************************************************************/ + +static NTSTATUS winbindd_add_memory_creds_internal(const char *username, const char *pass, BOOL store_pass) +{ + /* Shortcut to ensure we don't store if no mlock. */ +#if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK) + return NT_STATUS_OK; +#else + NTSTATUS status; + struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); + + if (memcredp) { + /* Already exists. Increment the reference count and replace stored creds. */ + memcredp->ref_count++; + DEBUG(10,("winbindd_add_memory_creds_internal: ref count for user %s is now %d\n", + username, memcredp->ref_count )); + return winbindd_replace_memory_creds_internal(memcredp, pass, store_pass); } - mem_ctx = talloc_init("winbindd_ccache_handling"); - if (mem_ctx == NULL) { + memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS); + if (!memcredp) { + return NT_STATUS_NO_MEMORY; + } + memcredp->username = talloc_strdup(memcredp, username); + if (!memcredp->username) { + talloc_destroy(memcredp); return NT_STATUS_NO_MEMORY; } - ZERO_STRUCTP(ccache_list); + status = store_memory_creds(memcredp, pass, store_pass); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + memcredp->ref_count = 1; + DLIST_ADD(memory_creds_list, memcredp); + + DEBUG(10,("winbindd_add_memory_creds_internal: added entry for user %s\n", + username )); return NT_STATUS_OK; +#endif +} + +/************************************************************* + Store users credentials in memory. If we also have a + struct WINBINDD_CCACHE_ENTRY for this username with a + refresh timer, then store the plaintext of the password + and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY. +*************************************************************/ + +NTSTATUS winbindd_add_memory_creds(const char *username, const char *pass) +{ + struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); + BOOL store_pass = False; + NTSTATUS status; + + if (entry && entry->event) { + store_pass = True; + } + + status = winbindd_add_memory_creds_internal(username, pass, store_pass); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (entry && store_pass) { + struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); + if (memcredp) { + entry->cred_ptr = memcredp; + } + } + + return status; +} + +/************************************************************* + Decrement the in-memory ref count - delete if zero. +*************************************************************/ + +NTSTATUS winbindd_delete_memory_creds(const char *username) +{ + struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); + NTSTATUS status = NT_STATUS_OK; + + if (!memcredp) { + DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n", + username )); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (memcredp->ref_count <= 0) { + DEBUG(0,("winbindd_delete_memory_creds: logic error. ref count for user %s = %d\n", + username, memcredp->ref_count)); + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + memcredp->ref_count--; + if (memcredp->ref_count <= 0) { + delete_memory_creds(memcredp); + DLIST_REMOVE(memory_creds_list, memcredp); + talloc_destroy(memcredp); + DEBUG(10,("winbindd_delete_memory_creds: deleted entry for user %s\n", + username)); + } else { + DEBUG(10,("winbindd_delete_memory_creds: entry for user %s ref_count now %d\n", + username, memcredp->ref_count)); + } + return status; +} + +/*********************************************************** + Replace the required creds with new ones (password change). +***********************************************************/ + +NTSTATUS winbindd_replace_memory_creds(const char *username, const char *pass) +{ + BOOL store_pass = False; + struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username); + + if (!memcredp) { + DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n", + username )); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (memcredp->len > NT_HASH_LEN + LM_HASH_LEN) { + store_pass = True; + } + + DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n", + username )); + + return winbindd_replace_memory_creds_internal(memcredp, pass, store_pass); } diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h index c6b6be33ed..88fe671114 100644 --- a/source3/nsswitch/winbindd_nss.h +++ b/source3/nsswitch/winbindd_nss.h @@ -456,22 +456,31 @@ struct winbindd_response { } extra_data; }; +struct WINBINDD_MEMORY_CREDS { + struct WINBINDD_MEMORY_CREDS *next, *prev; + const char *username; /* lookup key. */ + int ref_count; + size_t len; + unsigned char *nt_hash; /* Base pointer for the following 2 */ + unsigned char *lm_hash; + char *pass; +}; + struct WINBINDD_CCACHE_ENTRY { + struct WINBINDD_CCACHE_ENTRY *next, *prev; const char *principal_name; const char *ccname; const char *service; const char *username; const char *sid_string; - unsigned char *nt_hash; /* Base pointer for the following 2 */ - unsigned char *lm_hash; - char *pass; + struct WINBINDD_MEMORY_CREDS *cred_ptr; + int ref_count; uid_t uid; time_t create_time; time_t renew_until; BOOL refresh_tgt; time_t refresh_time; struct timed_event *event; - struct WINBINDD_CCACHE_ENTRY *next, *prev; }; #endif diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c index c93f4c98b6..f87749abed 100644 --- a/source3/nsswitch/winbindd_pam.c +++ b/source3/nsswitch/winbindd_pam.c @@ -405,6 +405,7 @@ static void setup_return_cc_name(struct winbindd_cli_state *state, const char *c Authenticate a user with a clear text password using Kerberos and fill up ccache if required **********************************************************************/ + static NTSTATUS winbindd_raw_kerberos_login(struct winbindd_domain *domain, struct winbindd_cli_state *state, NET_USER_INFO_3 **info3) @@ -630,9 +631,10 @@ failed: "%s\n", error_message(krb5_ret))); } - if (!NT_STATUS_IS_OK(remove_ccache_by_ccname(cc))) { + if (!NT_STATUS_IS_OK(remove_ccache(state->request.data.auth.user))) { DEBUG(3,("winbindd_raw_kerberos_login: " - "could not remove ccache\n")); + "could not remove ccache for user %s\n", + state->request.data.auth.user)); } done: @@ -1224,7 +1226,7 @@ process_result: DOM_SID user_sid; - /* In all codepaths were result == NT_STATUS_OK info3 must have + /* In all codepaths where result == NT_STATUS_OK info3 must have been initialized. */ if (!info3) { result = NT_STATUS_INTERNAL_ERROR; @@ -1265,27 +1267,40 @@ process_result: } - if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && - lp_winbind_offline_logon()) { + if ((state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) { + + /* Store in-memory creds for single-signon using ntlm_auth. */ + result = winbindd_add_memory_creds(state->request.data.auth.user, + state->request.data.auth.pass); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to store memory creds: %s\n", nt_errstr(result))); + goto done; + } - result = winbindd_store_creds(domain, + if (lp_winbind_offline_logon()) { + result = winbindd_store_creds(domain, state->mem_ctx, state->request.data.auth.user, state->request.data.auth.pass, info3, NULL); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); - goto done; - } + if (!NT_STATUS_IS_OK(result)) { + + /* Release refcount. */ + winbindd_delete_memory_creds(state->request.data.auth.user); + DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); + goto done; + } + } } - result = fillup_password_policy(domain, state); + result = fillup_password_policy(domain, state); - if (!NT_STATUS_IS_OK(result)) { - DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result))); - goto done; - } + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(result))); + goto done; + } } @@ -1739,17 +1754,26 @@ void winbindd_pam_chauthtok(struct winbindd_cli_state *state) } done: - if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN) && - lp_winbind_offline_logon()) { - NTSTATUS cred_ret; + if (NT_STATUS_IS_OK(result) && (state->request.flags & WBFLAG_PAM_CACHED_LOGIN)) { - cred_ret = winbindd_update_creds_by_name(contact_domain, + /* Update the single sign-on memory creds. */ + result = winbindd_replace_memory_creds(state->request.data.chauthtok.user, + newpass); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to replace memory creds: %s\n", nt_errstr(result))); + goto process_result; + } + + if (lp_winbind_offline_logon()) { + result = winbindd_update_creds_by_name(contact_domain, state->mem_ctx, user, newpass); - if (!NT_STATUS_IS_OK(cred_ret)) { - DEBUG(10,("Failed to store creds: %s\n", nt_errstr(cred_ret))); - goto process_result; /* FIXME: hm, risking inconsistant cache ? */ + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10,("Failed to store creds: %s\n", nt_errstr(result))); + goto process_result; + } } } @@ -1827,7 +1851,6 @@ enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain, struct winbindd_cli_state *state) { NTSTATUS result = NT_STATUS_NOT_SUPPORTED; - struct WINBINDD_CCACHE_ENTRY *entry; int ret; DEBUG(3, ("[%5lu]: pam dual logoff %s\n", (unsigned long)state->pid, @@ -1840,52 +1863,42 @@ enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain, #ifdef HAVE_KRB5 - /* what we need here is to find the corresponding krb5 ccache name *we* - * created for a given username and destroy it (as the user who created it) */ - - entry = get_ccache_by_username(state->request.data.logoff.user); - if (entry == NULL) { - DEBUG(10,("winbindd_pam_logoff: could not get ccname for user %s\n", - state->request.data.logoff.user)); - goto process_result; - } - - DEBUG(10,("winbindd_pam_logoff: found ccache [%s]\n", entry->ccname)); - - if (entry->uid < 0 || state->request.data.logoff.uid < 0) { + if (state->request.data.logoff.uid < 0) { DEBUG(0,("winbindd_pam_logoff: invalid uid\n")); goto process_result; } - if (entry->uid != state->request.data.logoff.uid) { - DEBUG(0,("winbindd_pam_logoff: uid's differ: %d != %d\n", - entry->uid, state->request.data.logoff.uid)); - goto process_result; - } - - if (!strcsequal(entry->ccname, state->request.data.logoff.krb5ccname)) { - DEBUG(0,("winbindd_pam_logoff: krb5ccnames differ: (daemon) %s != (client) %s\n", - entry->ccname, state->request.data.logoff.krb5ccname)); + /* what we need here is to find the corresponding krb5 ccache name *we* + * created for a given username and destroy it (as the user who created it) */ + + if (!ccache_entry_identical(state->request.data.logoff.user, + state->request.data.logoff.uid, + state->request.data.logoff.krb5ccname)) { + DEBUG(0,("winbindd_pam_logoff: cached entry differs.\n")); goto process_result; } - ret = ads_kdestroy(entry->ccname); + ret = ads_kdestroy(state->request.data.logoff.krb5ccname); if (ret) { DEBUG(0,("winbindd_pam_logoff: failed to destroy user ccache %s with: %s\n", - entry->ccname, error_message(ret))); + state->request.data.logoff.krb5ccname, error_message(ret))); } else { DEBUG(10,("winbindd_pam_logoff: successfully destroyed ccache %s for user %s\n", - entry->ccname, state->request.data.logoff.user)); - remove_ccache_by_ccname(entry->ccname); + state->request.data.logoff.krb5ccname, state->request.data.logoff.user)); } + remove_ccache(state->request.data.logoff.user); + result = krb5_to_nt_status(ret); #else result = NT_STATUS_NOT_SUPPORTED; #endif process_result: + + winbindd_delete_memory_creds(state->request.data.logoff.user); + state->response.data.auth.nt_status = NT_STATUS_V(result); fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result)); fstrcpy(state->response.data.auth.error_string, get_friendly_nt_error_msg(result)); |