From 7d436b1bd6bcca29aa9874adc11bdfb862139cd8 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 13 Dec 2010 22:38:21 +0100 Subject: Serialize requests of the same user in the krb5 provider --- src/providers/krb5/krb5_wait_queue.c | 209 +++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/providers/krb5/krb5_wait_queue.c (limited to 'src/providers/krb5/krb5_wait_queue.c') diff --git a/src/providers/krb5/krb5_wait_queue.c b/src/providers/krb5/krb5_wait_queue.c new file mode 100644 index 00000000..3863b1bd --- /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_ex(krb5_ctx, INIT_HASH_SIZE, + &krb5_ctx->wait_queue_hash, 0, 0, 0, 0, + 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; + } + + 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; +} + -- cgit