summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2006-08-26 02:53:45 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 11:38:53 -0500
commite627362622344f70cd12ded917fe9d82feb63f1a (patch)
treefe1030ad60424820b76b65d8a4c619f6b334326d
parent47e29ebe0975f63f972d137fca30fec843492f78 (diff)
downloadsamba-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.c4
-rw-r--r--source3/nsswitch/winbindd_cred_cache.c491
-rw-r--r--source3/nsswitch/winbindd_nss.h17
-rw-r--r--source3/nsswitch/winbindd_pam.c111
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));