/* SSSD Kerberos 5 Backend Module -- Request a TGT when the system gets online 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 #ifdef USE_KEYRING #include #include #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_SESSION_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; }