[SSSD] [PATCH] Serialize requests of the same user in the krb5 provider

Sumit Bose sbose at redhat.com
Fri Dec 17 16:33:26 UTC 2010


Hi,

I would like to propose this patch as an alternative fix for ticket
#533. This patch puts parallel auth requests of a user in a wait queue
and handles them one after the other. This will delay parallel auth
requests a bit but avoids all kind of race conditions I still can see in
the approach for creating the potential new ccache file at the beginning
of the request.

bye,
Sumit
-------------- next part --------------
From da3fc2237a07ed67ffd3f180128b994346b6ad7b Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 13 Dec 2010 22:38:21 +0100
Subject: [PATCH] Serialize requests of the same user in the krb5 provider

---
 Makefile.am                          |    2 +
 src/providers/krb5/krb5_auth.c       |   22 ++++
 src/providers/krb5/krb5_auth.h       |    6 +
 src/providers/krb5/krb5_common.h     |    2 +
 src/providers/krb5/krb5_wait_queue.c |  209 ++++++++++++++++++++++++++++++++++
 5 files changed, 241 insertions(+), 0 deletions(-)
 create mode 100644 src/providers/krb5/krb5_wait_queue.c

diff --git a/Makefile.am b/Makefile.am
index a98b589..d2cf3d0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -807,6 +807,7 @@ libsss_krb5_la_SOURCES = \
     src/providers/krb5/krb5_become_user.c \
     src/providers/krb5/krb5_delayed_online_authentication.c \
     src/providers/krb5/krb5_renew_tgt.c \
+    src/providers/krb5/krb5_wait_queue.c \
     src/providers/krb5/krb5_auth.c \
     src/providers/krb5/krb5_access.c \
     src/providers/krb5/krb5_child_handler.c \
@@ -854,6 +855,7 @@ libsss_ipa_la_SOURCES = \
     src/providers/krb5/krb5_become_user.c \
     src/providers/krb5/krb5_delayed_online_authentication.c \
     src/providers/krb5/krb5_renew_tgt.c \
+    src/providers/krb5/krb5_wait_queue.c \
     src/providers/krb5/krb5_common.c \
     src/providers/krb5/krb5_auth.c \
     src/providers/krb5/krb5_access.c \
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index a0ac0e9..ce3dea7 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -1093,6 +1093,7 @@ void krb5_pam_handler(struct be_req *be_req)
     struct pam_data *pd;
     struct krb5_ctx *krb5_ctx;
     int dp_err = DP_ERR_FATAL;
+    int ret;
 
     pd = talloc_get_type(be_req->req_data, struct pam_data);
     pd->pam_status = PAM_SYSTEM_ERR;
@@ -1108,6 +1109,19 @@ void krb5_pam_handler(struct be_req *be_req)
         case SSS_CMD_RENEW:
         case SSS_PAM_CHAUTHTOK_PRELIM:
         case SSS_PAM_CHAUTHTOK:
+            ret = add_to_wait_queue(be_req, pd, krb5_ctx);
+            if (ret == EOK) {
+                DEBUG(7, ("Request successfully added to wait queue "
+                          "of user [%s].\n", pd->user));
+                return;
+            } else if (ret == ENOENT) {
+                DEBUG(7, ("Wait queue of user [%s] is empty, "
+                          "running request immediately.\n", pd->user));
+            } else {
+                DEBUG(7, ("Failed to add request to wait queue of user [%s], "
+                          "running request immediately.\n", pd->user));
+            }
+
             req = krb5_auth_send(be_req, be_req->be_ctx->ev, be_req->be_ctx, pd,
                                  krb5_ctx);
             if (req == NULL) {
@@ -1154,6 +1168,7 @@ void krb5_auth_done(struct tevent_req *req)
     int pam_status;
     int dp_err;
     struct pam_data *pd;
+    struct krb5_ctx *krb5_ctx;
 
     pd = talloc_get_type(be_req->req_data, struct pam_data);
 
@@ -1166,6 +1181,13 @@ void krb5_auth_done(struct tevent_req *req)
         pd->pam_status = pam_status;
     }
 
+    krb5_ctx = get_krb5_ctx(be_req);
+    if (krb5_ctx == NULL) {
+        DEBUG(1, ("Kerberos context not available.\n"));
+    }
+
+    check_wait_queue(krb5_ctx, pd->user);
+
     krb_reply(be_req, dp_err, pd->pam_status);
 }
 
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 2f08ad9..0d6318d 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -66,6 +66,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                                   struct pam_data *pd,
                                   struct krb5_ctx *krb5_ctx);
 int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err);
+void krb5_auth_done(struct tevent_req *req);
 
 struct tevent_req *handle_child_send(TALLOC_CTX *mem_ctx,
                                      struct tevent_context *ev,
@@ -93,4 +94,9 @@ struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
                                     struct pam_data *pd,
                                     struct krb5_ctx *krb5_ctx);
 int krb5_access_recv(struct tevent_req *req, bool *access_allowed);
+
+/* krb5_wait_queue.c */
+errno_t add_to_wait_queue(struct be_req *be_req, struct pam_data *pd,
+                          struct krb5_ctx *krb5_ctx);
+void check_wait_queue(struct krb5_ctx *krb5_ctx, char *username);
 #endif /* __KRB5_AUTH_H__ */
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index a6fdd8b..c65ff74 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -117,6 +117,8 @@ struct krb5_ctx {
     struct deferred_auth_ctx *deferred_auth_ctx;
     struct renew_tgt_ctx *renew_tgt_ctx;
     bool use_fast;
+
+    hash_table_t *wait_queue_hash;
 };
 
 struct remove_info_files_ctx {
diff --git a/src/providers/krb5/krb5_wait_queue.c b/src/providers/krb5/krb5_wait_queue.c
new file mode 100644
index 0000000..7aff7e6
--- /dev/null
+++ b/src/providers/krb5/krb5_wait_queue.c
@@ -0,0 +1,209 @@
+/*
+    SSSD
+
+    Kerberos 5 Backend Module - Serialize the request of a user
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2010 Red Hat
+
+    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
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <tevent.h>
+#include <dhash.h>
+
+#include "src/providers/krb5/krb5_auth.h"
+
+#define INIT_HASH_SIZE 5
+
+struct queue_entry {
+    struct queue_entry *prev;
+    struct queue_entry *next;
+
+    struct be_req *be_req;
+    struct pam_data *pd;
+    struct krb5_ctx *krb5_ctx;
+};
+
+static void wait_queue_auth(struct tevent_context *ev, struct tevent_timer *te,
+                             struct timeval current_time, void *private_data)
+{
+    struct queue_entry *queue_entry = talloc_get_type(private_data,
+                                                      struct queue_entry);
+    struct tevent_req *req;
+
+    req = krb5_auth_send(queue_entry->be_req, queue_entry->be_req->be_ctx->ev,
+                         queue_entry->be_req->be_ctx, queue_entry->pd,
+                         queue_entry->krb5_ctx);
+    if (req == NULL) {
+        DEBUG(1, ("krb5_auth_send failed.\n"));
+    } else {
+        tevent_req_set_callback(req, krb5_auth_done, queue_entry->be_req);
+    }
+
+    talloc_zfree(queue_entry);
+}
+
+static void wait_queue_del_cb(hash_entry_t *entry, hash_destroy_enum type,
+                              void *pvt)
+{
+    struct queue_entry *head;
+
+    if (entry->value.type == HASH_VALUE_PTR) {
+        head = talloc_get_type(entry->value.ptr, struct queue_entry);
+        talloc_zfree(head);
+        return;
+    }
+
+    DEBUG(1, ("Unexpected value type [%d].\n", entry->value.type));
+}
+
+errno_t add_to_wait_queue(struct be_req *be_req, struct pam_data *pd,
+                          struct krb5_ctx *krb5_ctx)
+{
+    int ret;
+    hash_key_t key;
+    hash_value_t value;
+    struct queue_entry *head;
+    struct queue_entry *queue_entry;
+
+    if (krb5_ctx->wait_queue_hash == NULL) {
+        ret = sss_hash_create(krb5_ctx, INIT_HASH_SIZE,
+                              &krb5_ctx->wait_queue_hash,
+                              wait_queue_del_cb, NULL);
+        if (ret != EOK) {
+            DEBUG(1, ("sss_hash_create failed"));
+            return ret;
+        }
+    }
+
+    key.type = HASH_KEY_STRING;
+    key.str = pd->user;
+
+    ret = hash_lookup(krb5_ctx->wait_queue_hash, &key, &value);
+    switch (ret) {
+        case HASH_SUCCESS:
+            if (value.type != HASH_VALUE_PTR) {
+                DEBUG(1, ("Unexpected hash value type.\n"));
+                return EINVAL;
+            }
+
+            head = talloc_get_type(value.ptr, struct queue_entry);
+
+            queue_entry = talloc_zero(head, struct queue_entry);
+            if (queue_entry == NULL) {
+                DEBUG(1, ("talloc_zero failed.\n"));
+                return ENOMEM;
+            }
+
+            queue_entry->be_req = be_req;
+            queue_entry->pd = pd;
+            queue_entry->krb5_ctx = krb5_ctx;
+
+            DLIST_ADD_END(head, queue_entry, struct queue_entry *);
+
+            break;
+        case HASH_ERROR_KEY_NOT_FOUND:
+            value.type = HASH_VALUE_PTR;
+            head = talloc_zero(krb5_ctx->wait_queue_hash, struct queue_entry);
+            if (head == NULL) {
+                DEBUG(1, ("talloc_zero failed.\n"));
+                return ENOMEM;
+            }
+            value.ptr = head;
+
+            ret = hash_enter(krb5_ctx->wait_queue_hash, &key, &value);
+            if (ret != HASH_SUCCESS) {
+                DEBUG(1, ("hash_enter failed.\n"));
+                talloc_free(head);
+                return EIO;
+            }
+
+            break;
+        default:
+            DEBUG(1, ("hash_lookup failed.\n"));
+            return EIO;
+    }
+
+    if (head->next == NULL) {
+        return ENOENT;
+    } else {
+        return EOK;
+    }
+}
+
+void check_wait_queue(struct krb5_ctx *krb5_ctx, char *username)
+{
+    int ret;
+    hash_key_t key;
+    hash_value_t value;
+    struct queue_entry *head;
+    struct queue_entry *queue_entry;
+    struct tevent_timer *te;
+
+    if (krb5_ctx->wait_queue_hash == NULL) {
+        DEBUG(1, ("No wait queue available.\n"));
+        return;
+    }
+
+    key.type = HASH_KEY_STRING;
+    key.str = username;
+
+    ret = hash_lookup(krb5_ctx->wait_queue_hash, &key, &value);
+
+    switch (ret) {
+        case HASH_SUCCESS:
+            if (value.type != HASH_VALUE_PTR) {
+                DEBUG(1, ("Unexpected hash value type.\n"));
+                return EINVAL;
+            }
+
+            head = talloc_get_type(value.ptr, struct queue_entry);
+
+            if (head->next == NULL) {
+                DEBUG(7, ("Wait queue for user [%s] is empty.\n", username));
+            } else {
+                queue_entry = head->next;
+
+                DLIST_REMOVE(head, queue_entry);
+
+                te = tevent_add_timer(queue_entry->be_req->be_ctx->ev, krb5_ctx,
+                                      tevent_timeval_current(), wait_queue_auth,
+                                      queue_entry);
+                if (te == NULL) {
+                    DEBUG(1, ("tevent_add_timer failed.\n"));
+                } else {
+                    return;
+                }
+            }
+
+            ret = hash_delete(krb5_ctx->wait_queue_hash, &key);
+            if (ret != HASH_SUCCESS) {
+                DEBUG(1, ("Failed to remove wait queue for user [%s].\n",
+                          username));
+            }
+
+            break;
+        case HASH_ERROR_KEY_NOT_FOUND:
+            DEBUG(1, ("No wait queue for user [%s] found.\n", username));
+            break;
+        default:
+            DEBUG(1, ("hash_lookup failed.\n"));
+    }
+
+    return;
+}
+
-- 
1.7.3.2



More information about the sssd-devel mailing list