From da3fc2237a07ed67ffd3f180128b994346b6ad7b Mon Sep 17 00:00:00 2001 From: Sumit Bose 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 + + 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 . +*/ + +#include +#include + +#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