summaryrefslogtreecommitdiffstats
path: root/src/providers/krb5/krb5_delayed_online_authentication.c
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2010-04-19 11:59:09 +0200
committerStephen Gallagher <sgallagh@redhat.com>2010-05-26 15:14:40 -0400
commit02e38eae1b9cb5df2036a707dafd86f6047c17de (patch)
tree970b10c1df9bfe101a3d84ec1ff87dedd5364186 /src/providers/krb5/krb5_delayed_online_authentication.c
parent06c03627c81a5252420931383a68eb67ba551667 (diff)
downloadsssd-02e38eae1b9cb5df2036a707dafd86f6047c17de.tar.gz
sssd-02e38eae1b9cb5df2036a707dafd86f6047c17de.tar.xz
sssd-02e38eae1b9cb5df2036a707dafd86f6047c17de.zip
Add support for delayed kinit if offline
If the configuration option krb5_store_password_if_offline is set to true and the backend is offline the plain text user password is stored and used to request a TGT if the backend becomes online. If available the Linux kernel key retention service is used.
Diffstat (limited to 'src/providers/krb5/krb5_delayed_online_authentication.c')
-rw-r--r--src/providers/krb5/krb5_delayed_online_authentication.c354
1 files changed, 354 insertions, 0 deletions
diff --git a/src/providers/krb5/krb5_delayed_online_authentication.c b/src/providers/krb5/krb5_delayed_online_authentication.c
new file mode 100644
index 000000000..02f09919a
--- /dev/null
+++ b/src/providers/krb5/krb5_delayed_online_authentication.c
@@ -0,0 +1,354 @@
+/*
+ SSSD
+
+ Kerberos 5 Backend Module -- Request a TGT when the system gets online
+
+ Authors:
+ Sumit Bose <sbose@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 <security/pam_modules.h>
+#ifdef USE_KEYRING
+#include <sys/types.h>
+#include <keyutils.h>
+#endif
+
+#include "providers/krb5/krb5_auth.h"
+#include "dhash.h"
+#include "util/util.h"
+#include "util/find_uid.h"
+
+#define INITIAL_USER_TABLE_SIZE 10
+
+struct deferred_auth_ctx {
+ hash_table_t *user_table;
+ struct be_ctx *be_ctx;
+ struct tevent_context *ev;
+ struct krb5_ctx *krb5_ctx;
+};
+
+struct auth_data {
+ struct be_ctx *be_ctx;
+ struct krb5_ctx *krb5_ctx;
+ struct pam_data *pd;
+};
+
+static void *hash_talloc(const size_t size, void *pvt)
+{
+ return talloc_size(pvt, size);
+}
+
+static void hash_talloc_free(void *ptr, void *pvt)
+{
+ talloc_free(ptr);
+}
+
+static void authenticate_user_done(struct tevent_req *req);
+static void authenticate_user(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct auth_data *auth_data = talloc_get_type(private_data,
+ struct auth_data);
+ struct pam_data *pd = auth_data->pd;
+ struct tevent_req *req;
+
+ DEBUG_PAM_DATA(9, pd);
+
+ if (pd->authtok == NULL || pd->authtok_size == 0) {
+ DEBUG(1, ("Missing authtok for user [%s].\n", pd->user));
+ return;
+ }
+
+#ifdef USE_KEYRING
+ long keysize;
+ long keyrevoke;
+ int ret;
+ keysize = keyctl_read(pd->key_serial, (char *) pd->authtok,
+ pd->authtok_size);
+ keyrevoke = keyctl_revoke(pd->key_serial);
+ if (keysize == -1) {
+ ret = errno;
+ DEBUG(1, ("keyctl_read failed [%d][%s].\n", ret, strerror(ret)));
+ return;
+ } else if (keysize != pd->authtok_size) {
+ DEBUG(1, ("keyctl_read returned key with wrong size, "
+ "expect [%d] got [%d].\n", pd->authtok_size, keysize));
+ return;
+ }
+ if (keyrevoke == -1) {
+ ret = errno;
+ DEBUG(1, ("keyctl_revoke failed [%d][%s].\n", ret, strerror(ret)));
+ }
+#endif
+
+ req = krb5_auth_send(auth_data, ev, auth_data->be_ctx, auth_data->pd,
+ auth_data->krb5_ctx);
+ if (req == NULL) {
+ DEBUG(1, ("krb5_auth_send failed.\n"));
+ talloc_free(auth_data);
+ return;
+ }
+
+ tevent_req_set_callback(req, authenticate_user_done, auth_data);
+}
+
+static void authenticate_user_done(struct tevent_req *req) {
+ struct auth_data *auth_data = tevent_req_callback_data(req,
+ struct auth_data);
+ int ret;
+ int pam_status = PAM_SYSTEM_ERR;
+ int dp_err;
+
+ ret = krb5_auth_recv(req, &pam_status, &dp_err);
+ talloc_free(req);
+ if (ret) {
+ DEBUG(1, ("krb5_auth request failed.\n"));
+ } else {
+ if (pam_status == PAM_SUCCESS) {
+ DEBUG(4, ("Successfully authenticated user [%s].\n",
+ auth_data->pd->user));
+ } else {
+ DEBUG(1, ("Failed to authenticate user [%s].\n",
+ auth_data->pd->user));
+ }
+ }
+
+ talloc_free(auth_data);
+}
+
+static errno_t authenticate_stored_users(
+ struct deferred_auth_ctx *deferred_auth_ctx)
+{
+ int ret;
+ hash_table_t *uid_table;
+ struct hash_iter_context_t *iter;
+ hash_entry_t *entry;
+ hash_key_t key;
+ hash_value_t value;
+ struct pam_data *pd;
+ struct auth_data *auth_data;
+ struct tevent_timer *te;
+
+ ret = get_uid_table(deferred_auth_ctx, &uid_table);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("get_uid_table failed.\n"));
+ return ret;
+ }
+
+ iter = new_hash_iter_context(deferred_auth_ctx->user_table);
+ if (iter == NULL) {
+ DEBUG(1, ("new_hash_iter_context failed.\n"));
+ return EINVAL;
+ }
+
+ while ((entry = iter->next(iter)) != NULL) {
+ key.type = HASH_KEY_ULONG;
+ key.ul = entry->key.ul;
+ pd = talloc_get_type(entry->value.ptr, struct pam_data);
+
+ ret = hash_lookup(uid_table, &key, &value);
+
+ if (ret == HASH_SUCCESS) {
+ DEBUG(1, ("User [%s] is still logged in, "
+ "trying online authentication.\n", pd->user));
+
+ auth_data = talloc_zero(deferred_auth_ctx->be_ctx,
+ struct auth_data);
+ if (auth_data == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ } else {
+ auth_data->pd = talloc_steal(auth_data, pd);
+ auth_data->krb5_ctx = deferred_auth_ctx->krb5_ctx;
+ auth_data->be_ctx = deferred_auth_ctx->be_ctx;
+
+ te = tevent_add_timer(deferred_auth_ctx->ev,
+ auth_data, tevent_timeval_current(),
+ authenticate_user, auth_data);
+ if (te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed.\n"));
+ }
+ }
+ } else {
+ DEBUG(1, ("User [%s] is not logged in anymore, "
+ "discarding online authentication.\n", pd->user));
+ talloc_free(pd);
+ }
+
+ ret = hash_delete(deferred_auth_ctx->user_table,
+ &entry->key);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_delete failed [%s].\n",
+ hash_error_string(ret)));
+ }
+ }
+
+ talloc_free(iter);
+
+ return EOK;
+}
+
+static void delayed_online_authentication_callback(void *private_data)
+{
+ struct deferred_auth_ctx *deferred_auth_ctx =
+ talloc_get_type(private_data, struct deferred_auth_ctx);
+ int ret;
+
+ if (deferred_auth_ctx->user_table == NULL) {
+ DEBUG(1, ("Delayed online authentication activated, "
+ "but user table does not exists.\n"));
+ return;
+ }
+
+ DEBUG(5, ("Backend is online, starting delayed online authentication.\n"));
+ ret = authenticate_stored_users(deferred_auth_ctx);
+ if (ret != EOK) {
+ DEBUG(1, ("authenticate_stored_users failed.\n"));
+ }
+
+ return;
+}
+
+errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
+ struct pam_data *pd,
+ uid_t uid)
+{
+ int ret;
+ hash_key_t key;
+ hash_value_t value;
+ struct pam_data *new_pd;
+
+ if (krb5_ctx->deferred_auth_ctx == NULL) {
+ DEBUG(1, ("Missing context for delayed online authentication.\n"));
+ return EINVAL;
+ }
+
+ if (krb5_ctx->deferred_auth_ctx->user_table == NULL) {
+ DEBUG(1, ("user_table not available.\n"));
+ return EINVAL;
+ }
+
+ if (pd->authtok_size == 0 || pd->authtok == NULL) {
+ DEBUG(1, ("Missing authtok for user [%s].\n", pd->user));
+ return EINVAL;
+ }
+
+ ret = copy_pam_data(krb5_ctx->deferred_auth_ctx, pd, &new_pd);
+ if (ret != EOK) {
+ DEBUG(1, ("copy_pam_data failed\n"));
+ return ENOMEM;
+ }
+
+
+#ifdef USE_KEYRING
+ new_pd->key_serial = add_key("user", new_pd->user, new_pd->authtok,
+ new_pd->authtok_size, KEY_SPEC_THREAD_KEYRING);
+ if (new_pd->key_serial == -1) {
+ ret = errno;
+ DEBUG(1, ("add_key fialed [%d][%s].\n", ret, strerror(ret)));
+ talloc_free(new_pd);
+ return ret;
+ }
+ DEBUG(9, ("Saved authtok of user [%s] with serial [%ld].\n",
+ new_pd->user, new_pd->key_serial));
+ memset(new_pd->authtok, 0, new_pd->authtok_size);
+#endif
+
+ key.type = HASH_KEY_ULONG;
+ key.ul = uid;
+ value.type = HASH_VALUE_PTR;
+ value.ptr = new_pd;
+
+ ret = hash_enter(krb5_ctx->deferred_auth_ctx->user_table,
+ &key, &value);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("Cannot add user [%s] to table [%s], "
+ "delayed online authentication not possible.\n",
+ pd->user, hash_error_string(ret)));
+ talloc_free(new_pd);
+ return ENOMEM;
+ }
+
+ DEBUG(9, ("Added user [%s] successfully to "
+ "delayed online authentication.\n", pd->user));
+
+ return EOK;
+}
+
+errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
+ struct be_ctx *be_ctx,
+ struct tevent_context *ev)
+{
+ int ret;
+ hash_table_t *tmp_table;
+
+ ret = get_uid_table(krb5_ctx, &tmp_table);
+ if (ret != EOK) {
+ if (ret == ENOSYS) {
+ DEBUG(0, ("Delayed online auth was requested "
+ "on an unsupported system.\n"));
+ } else {
+ DEBUG(0, ("Delayed online auth was requested "
+ "but initialisation failed.\n"));
+ }
+ return ret;
+ }
+ ret = hash_destroy(tmp_table);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_destroy failed [%s].\n", hash_error_string(ret)));
+ return EFAULT;
+ }
+
+ krb5_ctx->deferred_auth_ctx = talloc_zero(krb5_ctx,
+ struct deferred_auth_ctx);
+ if (krb5_ctx->deferred_auth_ctx == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+
+ ret = hash_create_ex(INITIAL_USER_TABLE_SIZE,
+ &krb5_ctx->deferred_auth_ctx->user_table,
+ 0, 0, 0, 0, hash_talloc, hash_talloc_free,
+ krb5_ctx->deferred_auth_ctx,
+ NULL, NULL);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(1, ("hash_create_ex failed [%s]\n", hash_error_string(ret)));
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ krb5_ctx->deferred_auth_ctx->be_ctx = be_ctx;
+ krb5_ctx->deferred_auth_ctx->krb5_ctx = krb5_ctx;
+ krb5_ctx->deferred_auth_ctx->ev = ev;
+
+ ret = be_add_online_cb(krb5_ctx, be_ctx,
+ delayed_online_authentication_callback,
+ krb5_ctx->deferred_auth_ctx, NULL);
+ if (ret != EOK) {
+ DEBUG(1, ("be_add_online_cb failed.\n"));
+ goto fail;
+ }
+
+ /* TODO: add destructor */
+
+ return EOK;
+fail:
+ talloc_zfree(krb5_ctx->deferred_auth_ctx);
+ return ret;
+}