From cd8bcb840429a43aafcda44898cebd722ac7186b Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Thu, 3 Feb 2005 08:20:00 +0000 Subject: [PATCH] Initial version of KCM daemon git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@14544 ec53bebd-3082-4978-b11e-865c3cabbd6b --- kcm/.cvsignore | 1 + kcm/Makefile.am | 32 ++ kcm/acl.c | 179 ++++++++++ kcm/acquire.c | 142 ++++++++ kcm/cache.c | 636 +++++++++++++++++++++++++++++++++ kcm/client.c | 164 +++++++++ kcm/config.c | 349 ++++++++++++++++++ kcm/connect.c | 460 ++++++++++++++++++++++++ kcm/cursor.c | 151 ++++++++ kcm/events.c | 437 +++++++++++++++++++++++ kcm/glue.c | 280 +++++++++++++++ kcm/headers.h | 76 ++++ kcm/kcm_locl.h | 345 ++++++++++++++++++ kcm/log.c | 85 +++++ kcm/main.c | 109 ++++++ kcm/protocol.c | 1056 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ kcm/renew.c | 124 +++++++ 17 files changed, 4626 insertions(+) create mode 100644 kcm/.cvsignore create mode 100644 kcm/Makefile.am create mode 100644 kcm/acl.c create mode 100644 kcm/acquire.c create mode 100644 kcm/cache.c create mode 100644 kcm/client.c create mode 100644 kcm/config.c create mode 100644 kcm/connect.c create mode 100644 kcm/cursor.c create mode 100644 kcm/events.c create mode 100644 kcm/glue.c create mode 100644 kcm/headers.h create mode 100644 kcm/kcm_locl.h create mode 100644 kcm/log.c create mode 100644 kcm/main.c create mode 100644 kcm/protocol.c create mode 100644 kcm/renew.c diff --git a/kcm/.cvsignore b/kcm/.cvsignore new file mode 100644 index 000000000..70845e08e --- /dev/null +++ b/kcm/.cvsignore @@ -0,0 +1 @@ +Makefile.in diff --git a/kcm/Makefile.am b/kcm/Makefile.am new file mode 100644 index 000000000..9ff9bdf4e --- /dev/null +++ b/kcm/Makefile.am @@ -0,0 +1,32 @@ +# $Id$ + +include $(top_srcdir)/Makefile.am.common + +AM_CPPFLAGS += $(INCLUDE_krb4) $(INCLUDE_des) -I$(srcdir)/../lib/krb5 + +libexec_PROGRAMS = kcm + +kcm_SOURCES = \ + acl.c \ + acquire.c \ + cache.c \ + client.c \ + config.c \ + connect.c \ + cursor.c \ + events.c \ + glue.c \ + log.c \ + main.c \ + protocol.c \ + renew.c + +LDADD = $(top_builddir)/lib/hdb/libhdb.la \ + $(LIB_openldap) \ + $(top_builddir)/lib/krb5/libkrb5.la \ + $(LIB_krb4) \ + $(LIB_des) \ + $(top_builddir)/lib/asn1/libasn1.la \ + $(LIB_roken) + + diff --git a/kcm/acl.c b/kcm/acl.c new file mode 100644 index 000000000..c112ec64b --- /dev/null +++ b/kcm/acl.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior write_pitten permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHEwrite_pISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +krb5_error_code +kcm_access(krb5_context context, + kcm_client *client, + kcm_operation opcode, + kcm_ccache ccache) +{ + int read_p = 0; + int write_p = 0; + u_int16_t mask; + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + switch (opcode) { + case KCM_OP_INITIALIZE: + case KCM_OP_DESTROY: + case KCM_OP_STORE: + case KCM_OP_REMOVE_CRED: + case KCM_OP_SET_FLAGS: + case KCM_OP_CHOWN: + case KCM_OP_CHMOD: + case KCM_OP_GET_INITIAL_TICKET: + case KCM_OP_GET_TICKET: + write_p = 1; + read_p = 0; + break; + case KCM_OP_NOOP: + case KCM_OP_GET_NAME: + case KCM_OP_RESOLVE: + case KCM_OP_GEN_NEW: + case KCM_OP_RETRIEVE: + case KCM_OP_GET_PRINCIPAL: + case KCM_OP_GET_FIRST: + case KCM_OP_GET_NEXT: + case KCM_OP_END_GET: + case KCM_OP_MAX: + write_p = 0; + read_p = 1; + break; + } + + if (ccache->flags & KCM_FLAGS_OWNER_IS_SYSTEM) { + /* System caches cannot be reinitialized or destroyed by users */ + if (opcode == KCM_OP_INITIALIZE || + opcode == KCM_OP_DESTROY || + opcode == KCM_OP_REMOVE_CRED) { + ret = KRB5_FCC_PERM; + goto out; + } + + /* Let root always read system caches */ + if (client->uid == 0) { + ret = 0; + goto out; + } + } + + mask = 0; + + if (client->uid == ccache->uid) { + if (read_p) + mask |= S_IRUSR; + if (write_p) + mask |= S_IWUSR; + } else if (client->gid == ccache->gid) { + if (read_p) + mask |= S_IRGRP; + if (write_p) + mask |= S_IWGRP; + } else { + if (read_p) + mask |= S_IROTH; + if (write_p) + mask |= S_IWOTH; + } + + ret = ((ccache->mode & mask) == mask) ? 0 : KRB5_FCC_PERM; + +out: + if (ret) { + kcm_log(2, "Process %d is not permitted to call %s on cache %s", + client->pid, kcm_op2string(opcode), ccache->name); + } + + return ret; +} + +krb5_error_code +kcm_chmod(krb5_context context, + kcm_client *client, + kcm_ccache ccache, + u_int16_t mode) +{ + KCM_ASSERT_VALID(ccache); + + /* System cache mode can only be set at startup */ + if (ccache->flags & KCM_FLAGS_OWNER_IS_SYSTEM) + return KRB5_FCC_PERM; + + if (ccache->uid != client->uid) + return KRB5_FCC_PERM; + + if (ccache->gid != client->gid) + return KRB5_FCC_PERM; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + ccache->mode = mode; + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return 0; +} + +krb5_error_code +kcm_chown(krb5_context context, + kcm_client *client, + kcm_ccache ccache, + uid_t uid, + gid_t gid) +{ + KCM_ASSERT_VALID(ccache); + + /* System cache owner can only be set at startup */ + if (ccache->flags & KCM_FLAGS_OWNER_IS_SYSTEM) + return KRB5_FCC_PERM; + + if (ccache->uid != client->uid) + return KRB5_FCC_PERM; + + if (ccache->gid != client->gid) + return KRB5_FCC_PERM; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + ccache->uid = uid; + ccache->gid = gid; + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return 0; +} + diff --git a/kcm/acquire.c b/kcm/acquire.c new file mode 100644 index 000000000..a6f0790c4 --- /dev/null +++ b/kcm/acquire.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +/* + * Get a new ticket using a keytab/cached key and swap it into + * an existing redentials cache + */ + +krb5_error_code +kcm_ccache_acquire(krb5_context context, + kcm_ccache ccache, + krb5_creds **credp) +{ + krb5_error_code ret; + krb5_creds cred; + krb5_const_realm realm; + krb5_get_init_creds_opt opt; + krb5_ccache_data ccdata; + char *in_tkt_service = NULL; + + memset(&cred, 0, sizeof(cred)); + + KCM_ASSERT_VALID(ccache); + + /* We need a cached key or keytab to acquire credentials */ + if (ccache->flags & KCM_FLAGS_USE_CACHED_KEY) { + if (ccache->key.keyblock.keyvalue.length == 0) + krb5_abortx(context, + "kcm_ccache_acquire: KCM_FLAGS_USE_CACHED_KEY without key"); + } else if (ccache->flags & KCM_FLAGS_USE_KEYTAB) { + if (ccache->key.keytab == NULL) + krb5_abortx(context, + "kcm_ccache_acquire: KCM_FLAGS_USE_KEYTAB without keytab"); + } else { + kcm_log(0, "Cannot acquire initial credentials for cache %s without key", + ccache->name); + return KRB5_FCC_INTERNAL; + } + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + /* Fake up an internal ccache */ + kcm_internal_ccache(context, ccache, &ccdata); + + /* Now, actually acquire the creds */ + if (ccache->server != NULL) { + ret = krb5_unparse_name(context, ccache->server, &in_tkt_service); + if (ret) { + kcm_log(0, "Failed to unparse service principal name for cache %s: %s", + ccache->name, krb5_get_err_text(context, ret)); + goto out; + } + } + + realm = krb5_principal_get_realm(context, ccache->client); + + krb5_get_init_creds_opt_init(&opt); + krb5_get_init_creds_opt_set_default_flags(context, "kcm", realm, &opt); + if (ccache->tkt_life != 0) + krb5_get_init_creds_opt_set_tkt_life(&opt, ccache->tkt_life); + if (ccache->renew_life != 0) + krb5_get_init_creds_opt_set_renew_life(&opt, ccache->renew_life); + + if (ccache->flags & KCM_FLAGS_USE_CACHED_KEY) { + ret = krb5_get_init_creds_keyblock(context, + &cred, + ccache->client, + &ccache->key.keyblock, + 0, + in_tkt_service, + &opt); + } else { + ret = krb5_get_init_creds_keytab(context, + &cred, + ccache->client, + ccache->key.keytab, + 0, + in_tkt_service, + &opt); + } + + if (ret) { + kcm_log(0, "Failed to acquire credentials for cache %s: %s", + ccache->name, krb5_get_err_text(context, ret)); + if (in_tkt_service != NULL) + free(in_tkt_service); + goto out; + } + + if (in_tkt_service != NULL) + free(in_tkt_service); + + /* Swap them in */ + kcm_ccache_remove_creds_internal(context, ccache); + + ret = kcm_ccache_store_cred_internal(context, ccache, &cred, 0, credp); + if (ret) { + kcm_log(0, "Failed to store credentials for cache %s: %s", + ccache->name, krb5_get_err_text(context, ret)); + krb5_free_creds_contents(context, &cred); + goto out; + } + +out: + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + diff --git a/kcm/cache.c b/kcm/cache.c new file mode 100644 index 000000000..880f7e048 --- /dev/null +++ b/kcm/cache.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +static HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER; +static kcm_ccache_data *ccache_head = NULL; +static unsigned int ccache_nextid = 0; + +char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid) +{ + unsigned n; + char *name; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + n = ++ccache_nextid; + HEIMDAL_MUTEX_unlock(&ccache_mutex); + + asprintf(&name, "%d:%u", uid, n); + + return name; +} + +static krb5_error_code +kcm_ccache_resolve_internal(krb5_context context, + const char *name, + kcm_ccache *ccache) +{ + kcm_ccache p; + krb5_error_code ret; + + *ccache = NULL; + + ret = KRB5_FCC_NOFILE; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + + for (p = ccache_head; p != NULL; p = p->next) { + if ((p->flags & KCM_FLAGS_VALID) == 0) + continue; + if (strcmp(p->name, name) == 0) { + ret = 0; + break; + } + } + + if (ret == 0) { + kcm_retain_ccache(context, p); + *ccache = p; + } + + HEIMDAL_MUTEX_unlock(&ccache_mutex); + + return ret; +} + +krb5_error_code kcm_debug_ccache(krb5_context context) +{ + kcm_ccache p; + + for (p = ccache_head; p != NULL; p = p->next) { + char *cpn = NULL, *spn = NULL; + int ncreds = 0; + struct kcm_creds *k; + + if ((p->flags & KCM_FLAGS_VALID) == 0) { + kcm_log(7, "cache %08x: empty slot"); + continue; + } + + KCM_ASSERT_VALID(p); + + for (k = p->creds; k != NULL; k = k->next) + ncreds++; + + if (p->client != NULL) + krb5_unparse_name(context, p->client, &cpn); + if (p->server != NULL) + krb5_unparse_name(context, p->server, &spn); + + kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o " + "uid %d gid %d client %s server %s ncreds %d", + p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid, + (cpn == NULL) ? "" : cpn, + (spn == NULL) ? "" : spn, + ncreds); + + if (cpn != NULL) + free(cpn); + if (spn != NULL) + free(spn); + } + + return 0; +} + +static krb5_error_code +kcm_ccache_destroy_internal(krb5_context context, const char *name) +{ + kcm_ccache *p; + krb5_error_code ret; + + ret = KRB5_FCC_NOFILE; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + for (p = &ccache_head; *p != NULL; p = &(*p)->next) { + if (((*p)->flags & KCM_FLAGS_VALID) == 0) + continue; + if (strcmp((*p)->name, name) == 0) { + ret = 0; + break; + } + } + + if (ret) + goto out; + + kcm_release_ccache(context, p); + +out: + HEIMDAL_MUTEX_unlock(&ccache_mutex); + + return ret; +} + +static krb5_error_code +kcm_ccache_alloc(krb5_context context, + const char *name, + kcm_ccache *ccache) +{ + kcm_ccache slot = NULL, p; + krb5_error_code ret; + int new_slot = 0; + + *ccache = NULL; + + /* First, check for duplicates */ + HEIMDAL_MUTEX_lock(&ccache_mutex); + ret = 0; + for (p = ccache_head; p != NULL; p = p->next) { + if (p->flags & KCM_FLAGS_VALID) { + if (strcmp(p->name, name) == 0) { + ret = KRB5_CC_WRITE; + break; + } + } else if (slot == NULL) + slot = p; + } + + if (ret) + goto out; + + /* + * Then try and find an empty slot + * XXX we need to recycle slots for this to actually do anything + */ + if (slot == NULL) { + for (; p != NULL; p = p->next) { + if ((p->flags & KCM_FLAGS_VALID) == 0) { + slot = p; + break; + } + } + + if (slot == NULL) { + slot = (kcm_ccache_data *)malloc(sizeof(*slot)); + if (slot == NULL) { + ret = KRB5_CC_NOMEM; + goto out; + } + slot->next = ccache_head; + HEIMDAL_MUTEX_init(&slot->mutex); + new_slot = 1; + } + } + + slot->name = strdup(name); + if (slot->name == NULL) { + ret = KRB5_CC_NOMEM; + goto out; + } + + slot->refcnt = 1; + slot->flags = KCM_FLAGS_VALID; + slot->mode = S_IRUSR | S_IWUSR; + slot->uid = -1; + slot->gid = -1; + slot->client = NULL; + slot->server = NULL; + slot->creds = NULL; + slot->n_cursor = 0; + slot->cursors = NULL; + slot->key.keytab = NULL; + slot->tkt_life = 0; + slot->renew_life = 0; + + if (new_slot) + ccache_head = slot; + + *ccache = slot; + + HEIMDAL_MUTEX_unlock(&ccache_mutex); + return 0; + +out: + HEIMDAL_MUTEX_unlock(&ccache_mutex); + if (new_slot && slot != NULL) { + HEIMDAL_MUTEX_destroy(&slot->mutex); + free(slot); + } + return ret; +} + +krb5_error_code +kcm_ccache_remove_creds_internal(krb5_context context, + kcm_ccache ccache) +{ + struct kcm_creds *k; + struct kcm_cursor *c; + + k = ccache->creds; + while (k != NULL) { + struct kcm_creds *old; + + krb5_free_cred_contents(context, &k->cred); + old = k; + k = k->next; + free(old); + } + ccache->creds = NULL; + + /* remove anything that would have pointed into the creds too */ + + ccache->n_cursor = 0; + + c = ccache->cursors; + while (c != NULL) { + struct kcm_cursor *old; + + old = c; + c = c->next; + free(old); + } + ccache->cursors = NULL; + + return 0; +} + +krb5_error_code +kcm_ccache_remove_creds(krb5_context context, + kcm_ccache ccache) +{ + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = kcm_ccache_remove_creds_internal(context, ccache); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + +krb5_error_code +kcm_zero_ccache_data_internal(krb5_context context, + kcm_ccache_data *cache) +{ + if (cache->client != NULL) { + krb5_free_principal(context, cache->client); + cache->client = NULL; + } + + if (cache->server != NULL) { + krb5_free_principal(context, cache->server); + cache->server = NULL; + } + + kcm_ccache_remove_creds_internal(context, cache); + + return 0; +} + +krb5_error_code +kcm_zero_ccache_data(krb5_context context, + kcm_ccache cache) +{ + krb5_error_code ret; + + KCM_ASSERT_VALID(cache); + + HEIMDAL_MUTEX_lock(&cache->mutex); + ret = kcm_zero_ccache_data_internal(context, cache); + HEIMDAL_MUTEX_unlock(&cache->mutex); + + return ret; +} + +static krb5_error_code +kcm_free_ccache_data_internal(krb5_context context, + kcm_ccache_data *cache) +{ + KCM_ASSERT_VALID(cache); + + if (cache->name != NULL) { + free(cache->name); + cache->name = NULL; + } + + if (cache->flags & KCM_FLAGS_USE_KEYTAB) { + krb5_kt_close(context, cache->key.keytab); + cache->key.keytab = NULL; + } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) { + krb5_free_keyblock_contents(context, &cache->key.keyblock); + krb5_keyblock_zero(&cache->key.keyblock); + } + + cache->flags = 0; + cache->mode = 0; + cache->uid = -1; + cache->gid = -1; + + kcm_zero_ccache_data_internal(context, cache); + + cache->tkt_life = 0; + cache->renew_life = 0; + + cache->next = NULL; + cache->refcnt = 0; + + HEIMDAL_MUTEX_unlock(&cache->mutex); + HEIMDAL_MUTEX_destroy(&cache->mutex); + + return 0; +} + +krb5_error_code +kcm_retain_ccache(krb5_context context, + kcm_ccache ccache) +{ + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ccache->refcnt++; + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return 0; +} + +krb5_error_code +kcm_release_ccache(krb5_context context, + kcm_ccache *ccache) +{ + kcm_ccache c = *ccache; + krb5_error_code ret = 0; + + KCM_ASSERT_VALID(c); + + HEIMDAL_MUTEX_lock(&c->mutex); + if (c->refcnt == 1) { + ret = kcm_free_ccache_data_internal(context, c); + if (ret == 0) + free(c); + } else { + c->refcnt--; + HEIMDAL_MUTEX_unlock(&c->mutex); + } + + *ccache = NULL; + + return ret; +} + +krb5_error_code +kcm_ccache_gen_new(krb5_context context, + pid_t pid, + uid_t uid, + gid_t gid, + kcm_ccache *ccache) +{ + krb5_error_code ret; + char *name; + + name = kcm_ccache_nextid(pid, uid, gid); + if (name == NULL) { + return KRB5_CC_NOMEM; + } + + ret = kcm_ccache_new(context, name, ccache); + + free(name); + return ret; +} + +krb5_error_code +kcm_ccache_new(krb5_context context, + const char *name, + kcm_ccache *ccache) +{ + krb5_error_code ret; + + ret = kcm_ccache_alloc(context, name, ccache); + if (ret == 0) { + /* + * one reference is held by the linked list, + * one by the caller + */ + kcm_retain_ccache(context, *ccache); + } + + return ret; +} + +krb5_error_code +kcm_ccache_resolve(krb5_context context, + const char *name, + kcm_ccache *ccache) +{ + krb5_error_code ret; + + ret = kcm_ccache_resolve_internal(context, name, ccache); + + return ret; +} + +krb5_error_code +kcm_ccache_destroy(krb5_context context, + const char *name) +{ + krb5_error_code ret; + + ret = kcm_ccache_destroy_internal(context, name); + + return ret; +} + +krb5_error_code +kcm_ccache_destroy_if_empty(krb5_context context, + kcm_ccache ccache) +{ + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + if (ccache->creds == NULL) { + ret = kcm_ccache_destroy_internal(context, ccache->name); + } else + ret = 0; + + return ret; +} + +krb5_error_code +kcm_ccache_store_cred(krb5_context context, + kcm_ccache ccache, + krb5_creds *creds, + int copy) +{ + krb5_error_code ret; + krb5_creds *tmp; + + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = kcm_ccache_store_cred_internal(context, ccache, creds, copy, &tmp); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + +krb5_error_code +kcm_ccache_store_cred_internal(krb5_context context, + kcm_ccache ccache, + krb5_creds *creds, + int copy, + krb5_creds **credp) +{ + struct kcm_creds **c; + krb5_error_code ret; + + for (c = &ccache->creds; *c != NULL; c = &(*c)->next) + ; + + *c = (struct kcm_creds *)malloc(sizeof(struct kcm_creds)); + if (*c == NULL) { + return KRB5_CC_NOMEM; + } + + *credp = &(*c)->cred; + + if (copy) { + ret = krb5_copy_creds_contents(context, creds, *credp); + if (ret) { + free(*c); + *c = NULL; + } + } else { + **credp = *creds; + ret = 0; + } + + (*c)->next = NULL; + + return ret; +} + +static void +remove_cred(krb5_context context, + struct kcm_creds **c) +{ + struct kcm_creds *cred; + + cred = *c; + + *c = cred->next; + + krb5_free_cred_contents(context, &cred->cred); + free(cred); +} + +krb5_error_code +kcm_ccache_remove_cred_internal(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds) +{ + krb5_error_code ret; + struct kcm_creds **c; + + ret = KRB5_CC_NOTFOUND; + + for (c = &ccache->creds; *c != NULL; c = &(*c)->next) { + if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) { + remove_cred(context, c); + ret = 0; + } + } + + return ret; +} + +krb5_error_code +kcm_ccache_remove_cred(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds) +{ + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = kcm_ccache_remove_cred_internal(context, ccache, whichfields, mcreds); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + +krb5_error_code +kcm_ccache_retrieve_cred_internal(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds, + krb5_creds **creds) +{ + krb5_boolean match; + struct kcm_creds *c; + krb5_error_code ret; + + memset(creds, 0, sizeof(*creds)); + + ret = KRB5_CC_END; + + match = FALSE; + for (c = ccache->creds; c != NULL; c = c->next) { + match = krb5_compare_creds(context, whichfields, mcreds, &c->cred); + if (match) + break; + } + + if (match) { + ret = 0; + *creds = &c->cred; + } + + return ret; +} + +krb5_error_code +kcm_ccache_retrieve_cred(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds, + krb5_creds **credp) +{ + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = kcm_ccache_retrieve_cred_internal(context, ccache, + whichfields, mcreds, credp); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} diff --git a/kcm/client.c b/kcm/client.c new file mode 100644 index 000000000..efb6c61f5 --- /dev/null +++ b/kcm/client.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +krb5_error_code +kcm_ccache_resolve_client(krb5_context context, + kcm_client *client, + kcm_operation opcode, + const char *name, + kcm_ccache *ccache) +{ + krb5_error_code ret; + + ret = kcm_ccache_resolve(context, name, ccache); + if (ret) { + kcm_log(1, "Failed to resolve cache %s: %s", + name, krb5_get_err_text(context, ret)); + return ret; + } + + ret = kcm_access(context, client, opcode, *ccache); + if (ret) { + ret = KRB5_FCC_NOFILE; /* don't disclose */ + kcm_release_ccache(context, ccache); + } + + return ret; +} + +krb5_error_code +kcm_ccache_destroy_client(krb5_context context, + kcm_client *client, + const char *name) +{ + krb5_error_code ret; + kcm_ccache ccache; + + ret = kcm_ccache_resolve(context, name, &ccache); + if (ret) { + kcm_log(1, "Failed to resolve cache %s: %s", + name, krb5_get_err_text(context, ret)); + return ret; + } + + ret = kcm_access(context, client, KCM_OP_DESTROY, ccache); + if (ret) { + kcm_release_ccache(context, &ccache); + return ret; + } + + ret = kcm_ccache_destroy(context, ccache->name); + if (ret == 0) { + /* don't leave any events dangling */ + kcm_cleanup_events(context, ccache); + } + + kcm_release_ccache(context, &ccache); + return ret; +} + +krb5_error_code +kcm_ccache_new_client(krb5_context context, + kcm_client *client, + const char *name, + kcm_ccache *ccache_p) +{ + krb5_error_code ret; + kcm_ccache ccache; + + /* We insist the ccache name starts with UID or UID: */ + if (name_constraints != 0) { + char prefix[64]; + size_t prefix_len; + int bad = 1; + + snprintf(prefix, sizeof(prefix), "%d:", client->uid); + prefix_len = strlen(prefix); + + if (strncmp(name, prefix, prefix_len) == 0) + bad = 0; + else { + prefix[prefix_len - 1] = '\0'; + if (strcmp(name, prefix) == 0) + bad = 0; + } + + if (bad) + return KRB5_CC_BADNAME; + } + + ret = kcm_ccache_resolve(context, name, &ccache); + if (ret == 0) { + if (ccache->uid != client->uid || + ccache->gid != client->gid) + return KRB5_FCC_PERM; + } else if (ret != KRB5_FCC_NOFILE) { + return ret; + } + + if (ret == KRB5_FCC_NOFILE) { + ret = kcm_ccache_new(context, name, &ccache); + if (ret) { + kcm_log(1, "Failed to initialize cache %s: %s", + name, krb5_get_err_text(context, ret)); + return ret; + } + + /* bind to current client */ + ccache->uid = client->uid; + ccache->gid = client->gid; + } else { + ret = kcm_zero_ccache_data(context, ccache); + if (ret) { + kcm_log(1, "Failed to empty cache %s: %s", + name, krb5_get_err_text(context, ret)); + kcm_release_ccache(context, &ccache); + return ret; + } + kcm_cleanup_events(context, ccache); + } + + ret = kcm_access(context, client, KCM_OP_INITIALIZE, ccache); + if (ret) { + kcm_release_ccache(context, &ccache); + kcm_ccache_destroy(context, name); + return ret; + } + + *ccache_p = ccache; + return 0; +} + diff --git a/kcm/config.c b/kcm/config.c new file mode 100644 index 000000000..d37779240 --- /dev/null +++ b/kcm/config.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" +#include +#include + +RCSID("$Id$"); + +static const char *config_file; /* location of kcm config file */ + +size_t max_request = 0; /* maximal size of a request */ +char *socket_path = NULL; + +static char *max_request_str; /* `max_request' as a string */ + +#ifdef HAVE_DAEMON +int detach_from_console = -1; +#define DETACH_IS_DEFAULT FALSE +#endif + +static char *system_cache_name = NULL; +static char *system_keytab = NULL; +static char *system_principal = NULL; +static char *system_server = NULL; +static char *system_perms = NULL; +static char *system_user = NULL; +static char *system_group = NULL; + +static char *renew_life = NULL; +static char *ticket_life = NULL; + +int name_constraints = -1; + +static int help_flag; +static int version_flag; + +static struct getargs args[] = { + { + "cache-name", 0, arg_string, &system_cache_name, + "system cache name", "cachename" + }, + { + "config-file", 'c', arg_string, &config_file, + "location of config file", "file" + }, + { + "group", 'g', arg_string, &system_group, + "system cache group", "group" + }, + { + "max-request", 0, arg_string, &max_request, + "max size for a kcm-request", "size" + }, +#ifdef HAVE_DAEMON +#if DETACH_IS_DEFAULT + { + "detach", 'D', arg_negative_flag, &detach_from_console, + "don't detach from console" + }, +#else + { + "detach", 0 , arg_flag, &detach_from_console, + "detach from console" + }, +#endif +#endif + { "help", 'h', arg_flag, &help_flag }, + { + "system-principal", 'k', arg_string, &system_principal, + "system principal name", "principal" + }, + { + "lifetime", 'l', arg_string, &ticket_life, + "lifetime of system tickets", "time" + }, + { + "mode", 'm', arg_string, &system_perms, + "octal mode of system cache", "mode" + }, + { + "name-constraints", 'n', arg_negative_flag, &name_constraints, + "disable credentials cache name constraints" + }, + { + "renewable-life", 'r', arg_string, &renew_life, + "renewable lifetime of system tickets", "time" + }, + { + "socket-path", 's', arg_string, &socket_path, + "path to kcm domain socket", "path" + }, + { + "server", 'S', arg_string, &system_server, + "server to get system ticket for", "principal" + }, + { + "keytab", 't', arg_string, &system_keytab, + "system keytab name", "keytab" + }, + { + "user", 'u', arg_string, &system_user, + "system cache owner", "user" + }, + { "version", 'v', arg_flag, &version_flag } +}; + +static int num_args = sizeof(args) / sizeof(args[0]); + +static void +usage(int ret) +{ + arg_printusage (args, num_args, NULL, ""); + exit (ret); +} + +static int parse_owners(kcm_ccache ccache) +{ + uid_t uid = 0; + gid_t gid = 0; + struct passwd *pw; + struct group *gr; + int uid_p = 0; + int gid_p = 0; + + if (system_user != NULL) { + if (isdigit(system_user[0])) { + pw = getpwuid(atoi(system_user)); + } else { + pw = getpwnam(system_user); + } + if (pw == NULL) { + return errno; + } + + system_user = strdup(pw->pw_name); + if (system_user == NULL) { + return ENOMEM; + } + + uid = pw->pw_uid; uid_p = 1; + gid = pw->pw_gid; gid_p = 1; + } + + if (system_group != NULL) { + if (isdigit(system_group[0])) { + gr = getgrgid(atoi(system_group)); + } else { + gr = getgrnam(system_group); + } + if (gr == NULL) { + return errno; + } + + gid = gr->gr_gid; gid_p = 1; + } + + if (uid_p) + ccache->uid = uid; + else + ccache->uid = 0; /* geteuid() XXX */ + + if (gid_p) + ccache->gid = gid; + else + ccache->gid = 0; /* getegid() XXX */ + + return 0; +} + +static krb5_error_code +ccache_init_system(void) +{ + kcm_ccache ccache; + krb5_error_code ret; + + ret = kcm_ccache_new(kcm_context, + system_cache_name ? system_cache_name : "SYSTEM", + &ccache); + if (ret) + return ret; + + ccache->flags |= KCM_FLAGS_OWNER_IS_SYSTEM; + ccache->flags |= KCM_FLAGS_USE_KEYTAB; + + ret = parse_owners(ccache); + if (ret) + return ret; + + ret = krb5_parse_name(kcm_context, system_principal, &ccache->client); + if (ret) { + kcm_release_ccache(kcm_context, &ccache); + return ret; + } + + if (system_server != NULL) { + ret = krb5_parse_name(kcm_context, system_server, &ccache->server); + if (ret) { + kcm_release_ccache(kcm_context, &ccache); + return ret; + } + } + + if (system_keytab != NULL) { + ret = krb5_kt_resolve(kcm_context, system_keytab, &ccache->key.keytab); + } else { + ret = krb5_kt_default(kcm_context, &ccache->key.keytab); + } + if (ret) { + kcm_release_ccache(kcm_context, &ccache); + return ret; + } + + if (renew_life == NULL) + renew_life = "1 month"; + + if (renew_life != NULL) { + ccache->renew_life = parse_time(renew_life, "s"); + if (ccache->renew_life < 0) { + kcm_release_ccache(kcm_context, &ccache); + return EINVAL; + } + } + + if (ticket_life != NULL) { + ccache->tkt_life = parse_time(ticket_life, "s"); + if (ccache->tkt_life < 0) { + kcm_release_ccache(kcm_context, &ccache); + return EINVAL; + } + } + + if (system_perms != NULL) { + int mode; + + if (sscanf(system_perms, "%o", &mode) != 1) + return EINVAL; + + ccache->mode = mode; + } + + /* enqueue default actions for credentials cache */ + ret = kcm_ccache_enqueue_default(kcm_context, ccache, NULL); + + kcm_release_ccache(kcm_context, &ccache); /* retained by event queue */ + + return ret; +} + +void +kcm_configure(int argc, char **argv) +{ + krb5_error_code ret; + int optind = 0; + const char *p; + + while(getarg(args, num_args, argc, argv, &optind)) + warnx("error at argument `%s'", argv[optind]); + + if(help_flag) + usage (0); + + if (version_flag) { + print_version(NULL); + exit(0); + } + + argc -= optind; + argv += optind; + + if (argc != 0) + usage(1); + + { + char **files; + + if(config_file == NULL) + config_file = _PATH_KCM_CONF; + + ret = krb5_prepend_config_files_default(config_file, &files); + if (ret) + krb5_err(kcm_context, 1, ret, "getting configuration files"); + + ret = krb5_set_config_files(kcm_context, files); + krb5_free_config_files(files); + if(ret) + krb5_err(kcm_context, 1, ret, "reading configuration files"); + } + + if(max_request_str) + max_request = parse_bytes(max_request_str, NULL); + + if(max_request == 0){ + p = krb5_config_get_string (kcm_context, + NULL, + "kcm", + "max-request", + NULL); + if(p) + max_request = parse_bytes(p, NULL); + } + + if (system_principal != NULL) { + ret = ccache_init_system(); + if (ret) + krb5_err(kcm_context, 1, ret, "initializing system ccache"); + } + +#ifdef HAVE_DAEMON + if(detach_from_console == -1) + detach_from_console = krb5_config_get_bool_default(kcm_context, NULL, + DETACH_IS_DEFAULT, + "kcm", + "detach", NULL); +#endif + kcm_openlog(); + if(max_request == 0) + max_request = 64 * 1024; +} + diff --git a/kcm/connect.c b/kcm/connect.c new file mode 100644 index 000000000..06d1c59ca --- /dev/null +++ b/kcm/connect.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +struct descr { + int s; + int type; + char *path; + unsigned char *buf; + size_t size; + size_t len; + time_t timeout; + struct sockaddr_storage __ss; + struct sockaddr *sa; + socklen_t sock_len; + struct ucred peercred; +}; + +static void +init_descr(struct descr *d) +{ + memset(d, 0, sizeof(*d)); + d->sa = (struct sockaddr *)&d->__ss; + d->s = -1; +} + +/* + * re-initialize all `n' ->sa in `d'. + */ + +static void +reinit_descrs (struct descr *d, int n) +{ + int i; + + for (i = 0; i < n; ++i) + d[i].sa = (struct sockaddr *)&d[i].__ss; +} + +/* + * Create the socket (family, type, port) in `d' + */ + +static void +init_socket(struct descr *d) +{ + struct sockaddr_un sun; + struct sockaddr *sa = (struct sockaddr *)&sun; + krb5_socklen_t sa_size = sizeof(sun); + + init_descr (d); + + sun.sun_family = AF_UNIX; + + if (socket_path != NULL) + d->path = socket_path; + else + d->path = _PATH_KCM_SOCKET; + + strlcpy(sun.sun_path, d->path, sizeof(sun.sun_path)); + + d->s = socket(AF_UNIX, SOCK_STREAM, 0); + if (d->s < 0){ + krb5_warn(kcm_context, errno, "socket(%d, %d, 0)", AF_UNIX, SOCK_STREAM); + d->s = -1; + return; + } +#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR) + { + int one = 1; + setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); + } +#endif + d->type = SOCK_STREAM; + + unlink(d->path); + + if (bind(d->s, sa, sa_size) < 0) { + krb5_warn(kcm_context, errno, "bind %s", sun.sun_path); + close(d->s); + d->s = -1; + return; + } + + if (listen(d->s, SOMAXCONN) < 0) { + krb5_warn(kcm_context, errno, "listen %s", sun.sun_path); + close(d->s); + d->s = -1; + return; + } + + chmod(d->path, 0777); + + return; +} + +/* + * Allocate descriptors for all the sockets that we should listen on + * and return the number of them. + */ + +static int +init_sockets(struct descr **desc) +{ + struct descr *d; + size_t num = 0; + + d = (struct descr *)malloc(sizeof(*d)); + if (d == NULL) { + krb5_errx(kcm_context, 1, "malloc failed"); + } + + init_socket(d); + if (d->s != -1) { + kcm_log(5, "listening on domain socket %s", d->path); + num++; + } + + reinit_descrs (d, num); + *desc = d; + + return num; +} + +/* + * handle the request in `buf, len', from `addr' (or `from' as a string), + * sending a reply in `reply'. + */ + +static int +process_request(unsigned char *buf, + size_t len, + krb5_data *reply, + kcm_client *client) +{ + krb5_data request; + + if (len < 4) { + kcm_log(1, "malformed request from process %d (too short)", client->pid); + return -1; + } + + if (buf[0] != KCM_PROTOCOL_VERSION_MAJOR || + buf[1] != KCM_PROTOCOL_VERSION_MINOR) { + kcm_log(1, "incorrect protocol version %d.%d from process %d", + buf[0], buf[1], client->pid); + return -1; + } + + buf += 2; + len -= 2; + + /* buf is now pointing at opcode */ + + request.data = buf; + request.length = len; + + return kcm_dispatch(kcm_context, client, &request, reply); +} + +/* + * Handle the request in `buf, len' to socket `d' + */ + +static void +do_request(void *buf, size_t len, struct descr *d) +{ + krb5_error_code ret; + krb5_data reply; + kcm_client client; + + client.pid = d->peercred.pid; + client.uid = d->peercred.uid; + client.gid = d->peercred.gid; + + reply.length = 0; + + ret = process_request(buf, len, &reply, &client); + if (reply.length != 0) { + unsigned char len[4]; + + kcm_log(5, "sending %lu bytes to process %d", (unsigned long)reply.length, + client.pid); + + len[0] = (reply.length >> 24) & 0xff; + len[1] = (reply.length >> 16) & 0xff; + len[2] = (reply.length >> 8) & 0xff; + len[3] = reply.length & 0xff; + + if (sendto(d->s, len, sizeof(len), 0, NULL, 0) < 0) { + kcm_log (0, "sendto(%d): %d %s", d->peercred.pid, strerror(errno)); + krb5_data_free(&reply); + return; + } + if (sendto(d->s, reply.data, reply.length, 0, NULL, 0) < 0) { + kcm_log (0, "sendto(%d): %s", d->peercred.pid, strerror(errno)); + krb5_data_free(&reply); + return; + } + + krb5_data_free(&reply); + } + + if (ret) { + kcm_log(0, "Failed processing %lu byte request from process %d", + (unsigned long)len, d->peercred.pid); + } +} + +static void +clear_descr(struct descr *d) +{ + if(d->buf) + memset(d->buf, 0, d->size); + d->len = 0; + if(d->s != -1) + close(d->s); + d->s = -1; +} + +#define STREAM_TIMEOUT 4 + +/* + * accept a new stream connection on `d[parent]' and store it in `d[child]' + */ + +static void +add_new_stream (struct descr *d, int parent, int child) +{ + int s; + + if (child == -1) + return; + + d[child].sock_len = sizeof(d[child].__ss); + s = accept(d[parent].s, d[child].sa, &d[child].sock_len); + if(s < 0) { + krb5_warn(kcm_context, errno, "accept"); + return; + } + + if (s >= FD_SETSIZE) { + krb5_warnx(kcm_context, "socket FD too large"); + close (s); + return; + } + + d[child].s = s; + d[child].timeout = time(NULL) + STREAM_TIMEOUT; + d[child].type = SOCK_STREAM; +} + +/* + * Grow `d' to handle at least `n'. + * Return != 0 if fails + */ + +static int +grow_descr (struct descr *d, size_t n) +{ + if (d->size - d->len < n) { + unsigned char *tmp; + size_t grow; + + grow = max(1024, d->len + n); + if (d->size + grow > max_request) { + kcm_log(0, "Request exceeds max request size (%lu bytes).", + (unsigned long)d->size + grow); + clear_descr(d); + return -1; + } + tmp = realloc (d->buf, d->size + grow); + if (tmp == NULL) { + kcm_log(0, "Failed to re-allocate %lu bytes.", + (unsigned long)d->size + grow); + clear_descr(d); + return -1; + } + d->size += grow; + d->buf = tmp; + } + return 0; +} + +/* + * Handle incoming data to the stream socket in `d[index]' + */ + +static void +handle_stream(struct descr *d, int index, int min_free) +{ + unsigned char buf[1024]; + int n; + int ret = 0; + socklen_t peercredlen; + + if (d[index].timeout == 0) { + add_new_stream (d, index, min_free); + return; + } + + d[index].peercred.pid = 0; + d[index].peercred.uid = -1; + d[index].peercred.gid = -1; + + if (getsockopt(d[index].s, SOL_SOCKET, SO_PEERCRED, (void *)&d[index].peercred, + &peercredlen) != 0) { + krb5_warn(kcm_context, errno, "failed to determine peer identity"); + return; + } + + n = recvfrom(d[index].s, buf, sizeof(buf), 0, NULL, NULL); + if (n < 0) { + krb5_warn(kcm_context, errno, "recvfrom"); + return; + } else if (n == 0) { + krb5_warnx(kcm_context, "connection closed before end of data after %lu " + "bytes from process %d", + (unsigned long)d[index].len, d[index].peercred.pid); + clear_descr (d + index); + return; + } + if (grow_descr (&d[index], n)) + return; + memcpy(d[index].buf + d[index].len, buf, n); + d[index].len += n; + if (d[index].len > 4) { + krb5_storage *sp; + int32_t len; + + sp = krb5_storage_from_mem(d[index].buf, d[index].len); + if (sp == NULL) { + kcm_log (0, "krb5_storage_from_mem failed"); + ret = -1; + } else { + krb5_ret_int32(sp, &len); + krb5_storage_free(sp); + if (d[index].len - 4 >= len) { + memmove(d[index].buf, d[index].buf + 4, d[index].len - 4); + ret = 1; + } else + ret = 0; + } + } + if (ret < 0) + return; + else if (ret == 1) { + do_request(d[index].buf, d[index].len, &d[index]); + clear_descr(d + index); + } +} + +void +kcm_loop(void) +{ + struct descr *d; + int ndescr; + + ndescr = init_sockets(&d); + if (ndescr <= 0) + krb5_errx(kcm_context, 1, "No sockets!"); + while (exit_flag == 0){ + struct timeval tmout; + fd_set fds; + int min_free = -1; + int max_fd = 0; + int i; + + FD_ZERO(&fds); + for(i = 0; i < ndescr; i++) { + if (d[i].s >= 0){ + if(d[i].type == SOCK_STREAM && + d[i].timeout && d[i].timeout < time(NULL)) { + kcm_log(1, "Stream connection from %d expired after %lu bytes", + d[i].peercred.pid, (unsigned long)d[i].len); + clear_descr(&d[i]); + continue; + } + if (max_fd < d[i].s) + max_fd = d[i].s; + if (max_fd >= FD_SETSIZE) + krb5_errx(kcm_context, 1, "fd too large"); + FD_SET(d[i].s, &fds); + } else if (min_free < 0 || i < min_free) + min_free = i; + } + if (min_free == -1) { + struct descr *tmp; + tmp = realloc(d, (ndescr + 4) * sizeof(*d)); + if(tmp == NULL) + krb5_warnx(kcm_context, "No memory"); + else { + d = tmp; + reinit_descrs (d, ndescr); + memset(d + ndescr, 0, 4 * sizeof(*d)); + for(i = ndescr; i < ndescr + 4; i++) + init_descr (&d[i]); + min_free = ndescr; + ndescr += 4; + } + } + + tmout.tv_sec = STREAM_TIMEOUT; + tmout.tv_usec = 0; + switch (select(max_fd + 1, &fds, 0, 0, &tmout)){ + case 0: + kcm_run_events(kcm_context, time(NULL)); + break; + case -1: + if (errno != EINTR) + krb5_warn(kcm_context, errno, "select"); + break; + default: + for(i = 0; i < ndescr; i++) { + if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds)) { + if (d[i].type == SOCK_STREAM) + handle_stream(d, i, min_free); + } + } + kcm_run_events(kcm_context, time(NULL)); + } + } + if (d->path != NULL) + unlink(d->path); + free(d); +} + diff --git a/kcm/cursor.c b/kcm/cursor.c new file mode 100644 index 000000000..aa6f2777e --- /dev/null +++ b/kcm/cursor.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +krb5_error_code +kcm_cursor_new(krb5_context context, + pid_t pid, + kcm_ccache ccache, + u_int32_t *cursor) +{ + kcm_cursor **p; + krb5_error_code ret; + + *cursor = 0; + + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&ccache->mutex); + for (p = &ccache->cursors; *p != NULL; p = &(*p)->next) + ; + + *p = (kcm_cursor *)malloc(sizeof(kcm_cursor)); + if (*p == NULL) { + ret = KRB5_CC_NOMEM; + goto out; + } + + (*p)->pid = pid; + (*p)->key = ++ccache->n_cursor; + (*p)->credp = ccache->creds; + (*p)->next = NULL; + + *cursor = (*p)->key; + + ret = 0; + +out: + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + +krb5_error_code +kcm_cursor_find(krb5_context context, + pid_t pid, + kcm_ccache ccache, + u_int32_t key, + kcm_cursor **cursor) +{ + kcm_cursor *p; + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + if (key == 0) + return KRB5_CC_NOTFOUND; + + ret = KRB5_CC_END; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + for (p = ccache->cursors; p != NULL; p = p->next) { + if (p->key == key) { + if (p->pid != pid) + ret = KRB5_FCC_PERM; + else + ret = 0; + break; + } + } + + if (ret == 0) + *cursor = p; + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + +krb5_error_code +kcm_cursor_delete(krb5_context context, + pid_t pid, + kcm_ccache ccache, + u_int32_t key) +{ + kcm_cursor **p; + krb5_error_code ret; + + KCM_ASSERT_VALID(ccache); + + if (key == 0) + return KRB5_CC_NOTFOUND; + + ret = KRB5_CC_END; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + for (p = &ccache->cursors; *p != NULL; p = &(*p)->next) { + if ((*p)->key == key) { + if ((*p)->pid != pid) + ret = KRB5_FCC_PERM; + else + ret = 0; + break; + } + } + + if (ret == 0) { + kcm_cursor *x = *p; + + *p = x->next; + free(x); + } + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + diff --git a/kcm/events.c b/kcm/events.c new file mode 100644 index 000000000..b41602482 --- /dev/null +++ b/kcm/events.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +/* thread-safe in case we multi-thread later */ +static HEIMDAL_MUTEX events_mutex = HEIMDAL_MUTEX_INITIALIZER; +static kcm_event *events_head = NULL; +static time_t last_run = 0; + +static char *action_strings[] = { + "NONE", "ACQUIRE_CREDS", "RENEW_CREDS", + "DESTROY_CREDS", "DESTROY_EMPTY_CACHE" }; + +krb5_error_code +kcm_enqueue_event(krb5_context context, + kcm_event *event) +{ + krb5_error_code ret; + + if (event->action == KCM_EVENT_NONE) { + return 0; + } + + HEIMDAL_MUTEX_lock(&events_mutex); + ret = kcm_enqueue_event_internal(context, event); + HEIMDAL_MUTEX_unlock(&events_mutex); + + return ret; +} + +static void +print_times(time_t time, char buf[64]) +{ + if (time) + strftime(buf, 64, "%m-%dT%H:%M", gmtime(&time)); + else + strlcpy(buf, "never", 64); +} + +static void +log_event(kcm_event *event, char *msg) +{ + char fire_time[64], expire_time[64]; + + print_times(event->fire_time, fire_time); + print_times(event->expire_time, expire_time); + + kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s " + "backoff_time %d action %s cache %s", + msg, event, fire_time, event->fire_count, expire_time, + event->backoff_time, action_strings[event->action], + event->ccache->name); +} + +krb5_error_code +kcm_enqueue_event_internal(krb5_context context, + kcm_event *event) +{ + kcm_event **e; + + if (event->action == KCM_EVENT_NONE) + return 0; + + for (e = &events_head; *e != NULL; e = &(*e)->next) + ; + + *e = (kcm_event *)malloc(sizeof(kcm_event)); + if (*e == NULL) { + return KRB5_CC_NOMEM; + } + + (*e)->valid = 1; + (*e)->fire_time = event->fire_time; + (*e)->fire_count = 0; + (*e)->expire_time = event->expire_time; + (*e)->backoff_time = event->backoff_time; + + (*e)->action = event->action; + + kcm_retain_ccache(context, event->ccache); + (*e)->ccache = event->ccache; + (*e)->next = NULL; + + log_event(*e, "enqueuing"); + + return 0; +} + +/* + * Dump events list on SIGUSR2 + */ +krb5_error_code +kcm_debug_events(krb5_context context) +{ + kcm_event *e; + + for (e = events_head; e != NULL; e = e->next) + log_event(e, "debug"); + + return 0; +} + +krb5_error_code +kcm_enqueue_event_relative(krb5_context context, + kcm_event *event) +{ + krb5_error_code ret; + kcm_event e; + + e = *event; + e.backoff_time = e.fire_time; + e.fire_time += time(NULL); + + ret = kcm_enqueue_event(context, &e); + + return ret; +} + +static krb5_error_code +kcm_remove_event_internal(krb5_context context, + kcm_event **e) +{ + kcm_event *next; + + next = (*e)->next; + + (*e)->valid = 0; + (*e)->fire_time = 0; + (*e)->fire_count = 0; + (*e)->expire_time = 0; + (*e)->backoff_time = 0; + kcm_release_ccache(context, &(*e)->ccache); + (*e)->next = NULL; + free(*e); + + *e = next; + + return 0; +} + +static int +is_primary_credential_p(krb5_context context, + kcm_ccache ccache, + krb5_creds *newcred) +{ + krb5_flags whichfields; + + if (ccache->client == NULL) + return 0; + + if (newcred->client == NULL || + !krb5_principal_compare(context, ccache->client, newcred->client)) + return 0; + + /* XXX just checks whether it's the first credential in the cache */ + if (ccache->creds == NULL) + return 0; + + whichfields = KRB5_TC_MATCH_KEYTYPE | KRB5_TC_MATCH_FLAGS_EXACT | + KRB5_TC_MATCH_TIMES_EXACT | KRB5_TC_MATCH_AUTHDATA | + KRB5_TC_MATCH_2ND_TKT | KRB5_TC_MATCH_IS_SKEY; + + return krb5_compare_creds(context, whichfields, newcred, &ccache->creds->cred); +} + +/* + * Setup default events for a new credential + */ +static krb5_error_code +kcm_ccache_make_default_event(krb5_context context, + kcm_event *event, + krb5_creds *newcred) +{ + krb5_error_code ret = 0; + kcm_ccache ccache = event->ccache; + + event->fire_time = 0; + event->expire_time = 0; + event->backoff_time = KCM_EVENT_DEFAULT_BACKOFF_TIME; + + if (newcred == NULL) { + /* no creds, must be acquire creds request */ + if ((ccache->flags & KCM_MASK_KEY_PRESENT) == 0) { + kcm_log(0, "Cannot acquire credentials without a key"); + return KRB5_FCC_INTERNAL; + } + + event->fire_time = time(NULL); /* right away */ + event->action = KCM_EVENT_ACQUIRE_CREDS; + } else if (is_primary_credential_p(context, ccache, newcred)) { + if (newcred->flags.b.renewable) { + event->action = KCM_EVENT_RENEW_CREDS; + ccache->flags |= KCM_FLAGS_RENEWABLE; + } else { + if (ccache->flags & KCM_MASK_KEY_PRESENT) + event->action = KCM_EVENT_ACQUIRE_CREDS; + else + event->action = KCM_EVENT_NONE; + ccache->flags &= ~(KCM_FLAGS_RENEWABLE); + } + /* requeue with some slop factor */ + event->fire_time = newcred->times.endtime - KCM_EVENT_QUEUE_INTERVAL; + } else { + event->action = KCM_EVENT_NONE; + } + + return ret; +} + +krb5_error_code +kcm_ccache_enqueue_default(krb5_context context, + kcm_ccache ccache, + krb5_creds *newcred) +{ + kcm_event event; + krb5_error_code ret; + + memset(&event, 0, sizeof(event)); + event.ccache = ccache; + + ret = kcm_ccache_make_default_event(context, &event, newcred); + if (ret) + return ret; + + ret = kcm_enqueue_event_internal(context, &event); + if (ret) + return ret; + + return 0; +} + +krb5_error_code +kcm_remove_event(krb5_context context, + kcm_event *event) +{ + krb5_error_code ret; + kcm_event **e; + int found = 0; + + log_event(event, "removing"); + + HEIMDAL_MUTEX_lock(&events_mutex); + for (e = &events_head; *e != NULL; e = &(*e)->next) { + if (event == *e) { + *e = event->next; + found++; + break; + } + } + + if (!found) { + ret = KRB5_CC_NOTFOUND; + goto out; + } + + ret = kcm_remove_event_internal(context, &event); + +out: + HEIMDAL_MUTEX_unlock(&events_mutex); + + return ret; +} + +krb5_error_code +kcm_cleanup_events(krb5_context context, + kcm_ccache ccache) +{ + kcm_event **e; + + KCM_ASSERT_VALID(ccache); + + HEIMDAL_MUTEX_lock(&events_mutex); + + for (e = &events_head; *e != NULL; e = &(*e)->next) { + if ((*e)->valid && (*e)->ccache == ccache) { + kcm_remove_event_internal(context, e); + } + if (*e == NULL) + break; + } + + HEIMDAL_MUTEX_unlock(&events_mutex); + + return 0; +} + +static krb5_error_code +kcm_fire_event(krb5_context context, + kcm_event **e) +{ + kcm_event *event; + krb5_error_code ret; + krb5_creds *credp = NULL; + int oneshot = 1; + + event = *e; + + switch (event->action) { + case KCM_EVENT_ACQUIRE_CREDS: + ret = kcm_ccache_acquire(context, event->ccache, &credp); + oneshot = 0; + break; + case KCM_EVENT_RENEW_CREDS: + ret = kcm_ccache_refresh(context, event->ccache, &credp); + oneshot = 0; + break; + case KCM_EVENT_DESTROY_CREDS: + ret = kcm_ccache_destroy(context, event->ccache->name); + break; + case KCM_EVENT_DESTROY_EMPTY_CACHE: + ret = kcm_ccache_destroy_if_empty(context, event->ccache); + break; + default: + ret = KRB5_FCC_INTERNAL; + break; + } + + event->fire_count++; + + if (ret) { + /* Reschedule failed event for another time */ + event->fire_time += event->backoff_time; + if (event->backoff_time < KCM_EVENT_MAX_BACKOFF_TIME) + event->backoff_time *= 2; + + /* Remove it if it would never get executed */ + if (event->expire_time && + event->fire_time > event->expire_time) + kcm_remove_event_internal(context, e); + } else { + if (!oneshot) { + char *cpn; + + if (krb5_unparse_name(context, event->ccache->client, + &cpn)) + cpn = NULL; + + kcm_log(0, "%s credentials in cache %s for principal %s", + (event->action == KCM_EVENT_ACQUIRE_CREDS) ? + "Acquired" : "Renewed", + event->ccache->name, + (cpn != NULL) ? cpn : ""); + + if (cpn != NULL) + free(cpn); + + /* Succeeded, but possibly replaced with another event */ + ret = kcm_ccache_make_default_event(context, event, credp); + if (ret || event->action == KCM_EVENT_NONE) + oneshot = 1; + else + log_event(event, "requeuing"); + } + if (oneshot) + kcm_remove_event_internal(context, e); + } + + return ret; +} + +krb5_error_code +kcm_run_events(krb5_context context, + time_t now) +{ + krb5_error_code ret; + kcm_event **e; + + HEIMDAL_MUTEX_lock(&events_mutex); + + /* Only run event queue every N seconds */ + if (now < last_run + KCM_EVENT_QUEUE_INTERVAL) { + HEIMDAL_MUTEX_unlock(&events_mutex); + return 0; + } + + /* go through events list, fire and expire */ + for (e = &events_head; *e != NULL; e = &(*e)->next) { + if ((*e)->valid == 0) + continue; + + if (now >= (*e)->fire_time) { + ret = kcm_fire_event(context, e); + if (ret) { + kcm_log(1, "Could not fire event for cache %s: %s", + (*e)->ccache->name, krb5_get_err_text(context, ret)); + } + } else if ((*e)->expire_time && now >= (*e)->expire_time) { + ret = kcm_remove_event_internal(context, e); + if (ret) { + kcm_log(1, "Could not expire event for cache %s: %s", + (*e)->ccache->name, krb5_get_err_text(context, ret)); + } + } + + if (*e == NULL) + break; + } + + last_run = now; + + HEIMDAL_MUTEX_unlock(&events_mutex); + + return 0; +} + diff --git a/kcm/glue.c b/kcm/glue.c new file mode 100644 index 000000000..cd7547e0e --- /dev/null +++ b/kcm/glue.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +/* + * Server-side loopback glue for credentials cache operations; this + * must be initialized with kcm_internal_ccache(), it is not for real + * use. This entire file assumes the cache is locked, it does not do + * any concurrency checking for multithread applications. + */ + +#define KCMCACHE(X) ((kcm_ccache)(X)->data.data) +#define CACHENAME(X) (KCMCACHE(X)->name) + +static const char * +kcmss_get_name(krb5_context context, + krb5_ccache id) +{ + return CACHENAME(id); +} + +static krb5_error_code +kcmss_resolve(krb5_context context, krb5_ccache *id, const char *res) +{ + return KRB5_FCC_INTERNAL; +} + +static krb5_error_code +kcmss_gen_new(krb5_context context, krb5_ccache *id) +{ + return KRB5_FCC_INTERNAL; +} + +static krb5_error_code +kcmss_initialize(krb5_context context, + krb5_ccache id, + krb5_principal primary_principal) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + ret = kcm_zero_ccache_data_internal(context, c); + if (ret) + return ret; + + ret = krb5_copy_principal(context, primary_principal, + &c->client); + + return ret; +} + +static krb5_error_code +kcmss_close(krb5_context context, + krb5_ccache id) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + id->data.data = NULL; + id->data.length = 0; + + return ret; +} + +static krb5_error_code +kcmss_destroy(krb5_context context, + krb5_ccache id) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + ret = kcm_ccache_destroy(context, CACHENAME(id)); + + return ret; +} + +static krb5_error_code +kcmss_store_cred(krb5_context context, + krb5_ccache id, + krb5_creds *creds) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + krb5_creds *tmp; + + KCM_ASSERT_VALID(c); + + ret = kcm_ccache_store_cred_internal(context, c, creds, 1, &tmp); + + return ret; +} + +static krb5_error_code +kcmss_retrieve(krb5_context context, + krb5_ccache id, + krb5_flags which, + const krb5_creds *mcred, + krb5_creds *creds) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + krb5_creds *credp; + + KCM_ASSERT_VALID(c); + + ret = kcm_ccache_retrieve_cred_internal(context, c, which, + mcred, &credp); + if (ret) + return ret; + + ret = krb5_copy_creds_contents(context, credp, creds); + if (ret) + return ret; + + return 0; +} + +static krb5_error_code +kcmss_get_principal(krb5_context context, + krb5_ccache id, + krb5_principal *principal) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + ret = krb5_copy_principal(context, c->client, + principal); + + return ret; +} + +static krb5_error_code +kcmss_get_first (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + *cursor = c->creds; + + return (*cursor == NULL) ? KRB5_CC_END : 0; +} + +static krb5_error_code +kcmss_get_next (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *creds) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + ret = krb5_copy_creds_contents(context, + &((struct kcm_creds *)cursor)->cred, + creds); + if (ret) + return ret; + + *cursor = ((struct kcm_creds *)cursor)->next; + if (*cursor == 0) + ret = KRB5_CC_END; + + return ret; +} + +static krb5_error_code +kcmss_end_get (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + *cursor = NULL; + return 0; +} + +static krb5_error_code +kcmss_remove_cred(krb5_context context, + krb5_ccache id, + krb5_flags which, + krb5_creds *cred) +{ + krb5_error_code ret; + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + ret = kcm_ccache_remove_cred_internal(context, c, which, cred); + + return ret; +} + +static krb5_error_code +kcmss_set_flags(krb5_context context, + krb5_ccache id, + krb5_flags flags) +{ + return 0; +} + +static krb5_error_code +kcmss_get_version(krb5_context context, + krb5_ccache id) +{ + return 0; +} + +static const krb5_cc_ops krb5_kcmss_ops = { + "KCM", + kcmss_get_name, + kcmss_resolve, + kcmss_gen_new, + kcmss_initialize, + kcmss_destroy, + kcmss_close, + kcmss_store_cred, + kcmss_retrieve, + kcmss_get_principal, + kcmss_get_first, + kcmss_get_next, + kcmss_end_get, + kcmss_remove_cred, + kcmss_set_flags, + kcmss_get_version +}; + +krb5_error_code +kcm_internal_ccache(krb5_context context, + kcm_ccache c, + krb5_ccache id) +{ + id->ops = &krb5_kcmss_ops; + id->data.length = sizeof(*c); + id->data.data = c; + + return 0; +} + diff --git a/kcm/headers.h b/kcm/headers.h new file mode 100644 index 000000000..c642a37c4 --- /dev/null +++ b/kcm/headers.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __HEADERS_H__ +#define __HEADERS_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_UTIL_H +#include +#endif +#ifdef HAVE_LIBUTIL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#endif /* __HEADERS_H__ */ + diff --git a/kcm/kcm_locl.h b/kcm/kcm_locl.h new file mode 100644 index 000000000..24871740f --- /dev/null +++ b/kcm/kcm_locl.h @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#ifndef __KCM_LOCL_H__ +#define __KCM_LOCL_H__ + +#include "headers.h" + +#include + +#define KCM_LOG_REQUEST(_context, _client, _opcode) do { \ + kcm_log(1, "%s request by process %d/uid %d", \ + kcm_op2string(_opcode), (_client)->pid, (_client)->uid); \ + } while (0) + +#define KCM_LOG_REQUEST_NAME(_context, _client, _opcode, _name) do { \ + kcm_log(1, "%s request for cache %s by process %d/uid %d", \ + kcm_op2string(_opcode), (_name), (_client)->pid, (_client)->uid); \ + } while (0) + +/* Cache management */ + +#define KCM_FLAGS_VALID 0x0001 +#define KCM_FLAGS_USE_KEYTAB 0x0002 +#define KCM_FLAGS_RENEWABLE 0x0004 +#define KCM_FLAGS_OWNER_IS_SYSTEM 0x0008 +#define KCM_FLAGS_USE_CACHED_KEY 0x0010 + +#define KCM_MASK_KEY_PRESENT ( KCM_FLAGS_USE_KEYTAB | \ + KCM_FLAGS_USE_CACHED_KEY ) + +struct kcm_ccache_data; +struct kcm_creds; + +typedef struct kcm_cursor { + pid_t pid; + u_int32_t key; + struct kcm_creds *credp; /* pointer to next credential */ + struct kcm_cursor *next; +} kcm_cursor; + +typedef struct kcm_ccache_data { + char *name; + unsigned refcnt; + u_int16_t flags; + u_int16_t mode; + uid_t uid; + gid_t gid; + krb5_principal client; /* primary client principal */ + krb5_principal server; /* primary server principal (TGS if NULL) */ + struct kcm_creds { + krb5_creds cred; /* XXX would be useful for have ACLs on creds */ + struct kcm_creds *next; + } *creds; + u_int32_t n_cursor; + kcm_cursor *cursors; + krb5_deltat tkt_life; + krb5_deltat renew_life; + union { + krb5_keytab keytab; + krb5_keyblock keyblock; + } key; + HEIMDAL_MUTEX mutex; + struct kcm_ccache_data *next; +} kcm_ccache_data; + +#define KCM_ASSERT_VALID(_ccache) do { \ + if (((_ccache)->flags & KCM_FLAGS_VALID) == 0) \ + krb5_abortx(context, "kcm_free_ccache_data: ccache invalid"); \ + else if ((_ccache)->refcnt == 0) \ + krb5_abortx(context, "kcm_free_ccache_data: ccache refcnt == 0"); \ + } while (0) + +typedef kcm_ccache_data *kcm_ccache; + +char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid); + +/* foo_internal routines assume caller has acquired lock */ +/* locking is there in case we eventually multithread */ + +krb5_error_code kcm_debug_ccache(krb5_context context); + +krb5_error_code kcm_zero_ccache_data(krb5_context context, + kcm_ccache cache); + +krb5_error_code kcm_zero_ccache_data_internal(krb5_context context, + kcm_ccache_data *cache); + +krb5_error_code kcm_retain_ccache(krb5_context context, + kcm_ccache ccache); + +krb5_error_code kcm_release_ccache(krb5_context context, + kcm_ccache *ccache); + +krb5_error_code kcm_ccache_new(krb5_context context, + const char *name, + kcm_ccache *ccache); + +krb5_error_code kcm_ccache_destroy_if_empty(krb5_context context, + kcm_ccache ccache); + +krb5_error_code kcm_ccache_acquire(krb5_context context, + kcm_ccache ccache, + krb5_creds **credp); + +krb5_error_code kcm_ccache_refresh(krb5_context context, + kcm_ccache ccache, + krb5_creds **credp); + +krb5_error_code kcm_ccache_gen_new(krb5_context context, + pid_t pid, + uid_t uid, + gid_t gid, + kcm_ccache *ccache); + +krb5_error_code kcm_ccache_resolve(krb5_context context, + const char *name, + kcm_ccache *ccache); + +krb5_error_code kcm_ccache_destroy(krb5_context context, + const char *name); + +krb5_error_code kcm_ccache_store_cred_internal(krb5_context context, + kcm_ccache ccache, + krb5_creds *creds, + int copy, + krb5_creds **out); + +krb5_error_code kcm_ccache_store_cred(krb5_context context, + kcm_ccache ccache, + krb5_creds *creds, + int copy); + +krb5_error_code kcm_ccache_remove_cred_internal(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds); + +krb5_error_code kcm_ccache_remove_cred(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds); + +krb5_error_code kcm_ccache_retrieve_cred_internal(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds, + krb5_creds **creds); + +krb5_error_code kcm_ccache_retrieve_cred(krb5_context context, + kcm_ccache ccache, + krb5_flags whichfields, + const krb5_creds *mcreds, + krb5_creds **credp); + +krb5_error_code kcm_ccache_remove_creds_internal(krb5_context context, + kcm_ccache ccache); + +krb5_error_code kcm_ccache_remove_creds(krb5_context context, + kcm_ccache ccache); + +/* Credentials enumeration */ + +krb5_error_code kcm_cursor_new(krb5_context context, + pid_t pid, + kcm_ccache ccache, + u_int32_t *cursor); + +krb5_error_code kcm_cursor_find(krb5_context context, + pid_t pid, + kcm_ccache ccache, + u_int32_t key, + kcm_cursor **cursor); + +krb5_error_code kcm_cursor_delete(krb5_context context, + pid_t pid, + kcm_ccache ccache, + u_int32_t key); + +/* Event management */ + +typedef struct kcm_event { + int valid; + time_t fire_time; + unsigned fire_count; + time_t expire_time; + time_t backoff_time; + enum { + KCM_EVENT_NONE = 0, + KCM_EVENT_ACQUIRE_CREDS, + KCM_EVENT_RENEW_CREDS, + KCM_EVENT_DESTROY_CREDS, + KCM_EVENT_DESTROY_EMPTY_CACHE + } action; + kcm_ccache ccache; + struct kcm_event *next; +} kcm_event; + +/* wakeup interval for event queue */ +#define KCM_EVENT_QUEUE_INTERVAL 60 +#define KCM_EVENT_DEFAULT_BACKOFF_TIME 5 +#define KCM_EVENT_MAX_BACKOFF_TIME (12 * 60 * 60) + +krb5_error_code kcm_ccache_enqueue_default(krb5_context context, + kcm_ccache ccache, + krb5_creds *newcred); + +krb5_error_code kcm_enqueue_event(krb5_context context, + kcm_event *event); + +/* don't grab lock */ +krb5_error_code kcm_enqueue_event_internal(krb5_context context, + kcm_event *event); + +/* fire time is relative to now */ +krb5_error_code kcm_enqueue_event_relative(krb5_context context, + kcm_event *event); + +krb5_error_code kcm_remove_event(krb5_context context, + kcm_event *event); + +krb5_error_code kcm_cleanup_events(krb5_context context, + kcm_ccache ccache); + +krb5_error_code kcm_run_events(krb5_context context, + time_t now); + +krb5_error_code kcm_debug_events(krb5_context context); + +/* Operation dispatch */ + +/* Request format is LENGTH | MAJOR | MINOR | OPERATION | request */ +/* Response format is LENGTH | STATUS | response */ + +typedef struct kcm_client { + pid_t pid; + uid_t uid; + gid_t gid; +} kcm_client; + +/* Access-checked cache management */ +krb5_error_code kcm_ccache_resolve_client(krb5_context context, + kcm_client *client, + kcm_operation opcode, + const char *name, + kcm_ccache *ccache); + +krb5_error_code kcm_ccache_destroy_client(krb5_context context, + kcm_client *client, + const char *name); + +krb5_error_code kcm_ccache_new_client(krb5_context context, + kcm_client *client, + const char *name, + kcm_ccache *ccache); + +const char *kcm_op2string(kcm_operation operation); + +/* Dispatch table */ +/* passed in OPERATION | ... ; returns STATUS | ... */ +typedef krb5_error_code (*kcm_method)(krb5_context, kcm_client *, kcm_operation, krb5_storage *, krb5_storage *); + +krb5_error_code kcm_dispatch(krb5_context context, + kcm_client *sd, + krb5_data *request, + krb5_data *response); + +/* Access checking */ +krb5_error_code kcm_access(krb5_context context, + kcm_client *client, + kcm_operation opcode, + kcm_ccache ccache); + +krb5_error_code kcm_chown(krb5_context context, + kcm_client *client, + kcm_ccache ccache, + uid_t uid, + gid_t gid); + +krb5_error_code kcm_chmod(krb5_context context, + kcm_client *client, + kcm_ccache ccache, + u_int16_t mode); + +krb5_error_code +kcm_internal_ccache(krb5_context context, + kcm_ccache c, + krb5_ccache id); + +void kcm_openlog(void); +void kcm_log(int level, const char *fmt, ...); +char *kcm_log_msg(int level, const char *fmt, ...); +char *kcm_log_msg_va(int level, const char *fmt, va_list ap); + +#define DEFAULT_LOG_DEST "0/FILE:" LOCALSTATEDIR "/log/kcmd.log" +#define _PATH_KCM_CONF SYSCONFDIR "/kcm.conf" + +void kcm_configure(int argc, char **argv); +void kcm_loop(void); + +extern krb5_context kcm_context; +extern char *socket_path; +extern size_t max_request; +extern sig_atomic_t exit_flag; +extern int name_constraints; + +#if 0 +extern const krb5_cc_ops krb5_kcmss_ops; +#endif + +#endif /* __KCM_LOCL_H__ */ + diff --git a/kcm/log.c b/kcm/log.c new file mode 100644 index 000000000..387d06073 --- /dev/null +++ b/kcm/log.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1997, 1998, 2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +static krb5_log_facility *logf; + +void +kcm_openlog(void) +{ + char **s = NULL, **p; + krb5_initlog(kcm_context, "kcm", &logf); + s = krb5_config_get_strings(kcm_context, NULL, "kcm", "logging", NULL); + if(s == NULL) + s = krb5_config_get_strings(kcm_context, NULL, "logging", "kcm", NULL); + if(s){ + for(p = s; *p; p++) + krb5_addlog_dest(kcm_context, logf, *p); + krb5_config_free_strings(s); + }else + krb5_addlog_dest(kcm_context, logf, DEFAULT_LOG_DEST); + krb5_set_warn_dest(kcm_context, logf); +} + +char* +kcm_log_msg_va(int level, const char *fmt, va_list ap) +{ + char *msg; + krb5_vlog_msg(kcm_context, logf, &msg, level, fmt, ap); + return msg; +} + +char* +kcm_log_msg(int level, const char *fmt, ...) +{ + va_list ap; + char *s; + va_start(ap, fmt); + s = kcm_log_msg_va(level, fmt, ap); + va_end(ap); + return s; +} + +void +kcm_log(int level, const char *fmt, ...) +{ + va_list ap; + char *s; + va_start(ap, fmt); + s = kcm_log_msg_va(level, fmt, ap); + if(s) free(s); + va_end(ap); +} diff --git a/kcm/main.c b/kcm/main.c new file mode 100644 index 000000000..5926d6716 --- /dev/null +++ b/kcm/main.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +sig_atomic_t exit_flag = 0; + +#ifdef HAVE_DAEMON +extern int detach_from_console; +#endif + +krb5_context kcm_context = NULL; + +static RETSIGTYPE +sigterm(int sig) +{ + exit_flag = 1; +} + +static RETSIGTYPE +sigusr1(int sig) +{ + kcm_debug_ccache(kcm_context); +} + +static RETSIGTYPE +sigusr2(int sig) +{ + kcm_debug_events(kcm_context); +} + +int +main(int argc, char **argv) +{ + krb5_error_code ret; + setprogname(argv[0]); + + ret = krb5_init_context(&kcm_context); + if (ret) { + errx (1, "krb5_init_context failed: %d", ret); + return ret; + } + + kcm_configure(argc, argv); + +#ifdef HAVE_SIGACTION + { + struct sigaction sa; + + sa.sa_flags = 0; + sa.sa_handler = sigterm; + sigemptyset(&sa.sa_mask); + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + sa.sa_handler = sigusr1; + sigaction(SIGUSR1, &sa, NULL); + + sa.sa_handler = sigusr2; + sigaction(SIGUSR2, &sa, NULL); + } +#else + signal(SIGINT, sigterm); + signal(SIGTERM, sigterm); + signal(SIGUSR1, sigusr1); + signal(SIGUSR2, sigusr2); +#endif +#ifdef HAVE_DAEMON + if (detach_from_console) + daemon(0, 0); +#endif + pidfile(NULL); + kcm_loop(); + krb5_free_context(kcm_context); + return 0; +} diff --git a/kcm/protocol.c b/kcm/protocol.c new file mode 100644 index 000000000..7afd8ad1d --- /dev/null +++ b/kcm/protocol.c @@ -0,0 +1,1056 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +static krb5_error_code +kcm_op_noop(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + KCM_LOG_REQUEST(context, client, opcode); + + return 0; +} + +/* + * Request: + * NameZ + * Response: + * NameZ + * + */ +static krb5_error_code +kcm_op_get_name(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) + +{ + krb5_error_code ret; + char *name = NULL; + kcm_ccache ccache; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + ret = krb5_store_stringz(response, ccache->name); + if (ret) { + kcm_release_ccache(context, &ccache); + free(name); + return ret; + } + + free(name); + kcm_release_ccache(context, &ccache); + return 0; +} + +/* + * Request: + * + * Response: + * NameZ + */ +static krb5_error_code +kcm_op_gen_new(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + char *name; + + KCM_LOG_REQUEST(context, client, opcode); + + name = kcm_ccache_nextid(client->pid, client->uid, client->gid); + if (name == NULL) { + return KRB5_CC_NOMEM; + } + + ret = krb5_store_stringz(response, name); + free(name); + + return ret; +} + +/* + * Request: + * NameZ + * Principal + * + * Response: + * + */ +static krb5_error_code +kcm_op_initialize(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + kcm_ccache ccache; + krb5_principal principal; + krb5_error_code ret; + char *name; +#if 0 + kcm_event event; +#endif + + KCM_LOG_REQUEST(context, client, opcode); + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + ret = krb5_ret_principal(request, &principal); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_new_client(context, client, name, &ccache); + if (ret) { + free(name); + krb5_free_principal(context, principal); + return ret; + } + + ccache->client = principal; + + free(name); + +#if 0 + /* + * Create a new credentials cache. To mitigate DoS attacks we will + * expire it in 30 minutes unless it has some credentials added + * to it + */ + + event.fire_time = 30 * 60; + event.expire_time = 0; + event.backoff_time = 0; + event.action = KCM_EVENT_DESTROY_EMPTY_CACHE; + event.ccache = ccache; + + ret = kcm_enqueue_event_relative(context, &event); +#endif + + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * + * Response: + * + */ +static krb5_error_code +kcm_op_destroy(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = kcm_ccache_destroy_client(context, client, name); + + free(name); + + return ret; +} + +/* + * Request: + * NameZ + * Creds + * + * Response: + * + */ +static krb5_error_code +kcm_op_store(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_creds creds; + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_creds(request, &creds); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + krb5_free_creds_contents(context, &creds); + return ret; + } + + ret = kcm_ccache_store_cred(context, ccache, &creds, 0); + if (ret) { + free(name); + krb5_free_creds_contents(context, &creds); + kcm_release_ccache(context, &ccache); + return ret; + } + + kcm_ccache_enqueue_default(context, ccache, &creds); + + free(name); + kcm_release_ccache(context, &ccache); + + return 0; +} + +/* + * Request: + * NameZ + * WhichFields + * MatchCreds + * + * Response: + * Creds + * + */ +static krb5_error_code +kcm_op_retrieve(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + u_int32_t flags; + krb5_creds mcreds; + krb5_error_code ret; + kcm_ccache ccache; + char *name; + krb5_creds *credp; + int free_creds = 0; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &flags); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_creds_tag(request, &mcreds); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + krb5_free_creds_contents(context, &mcreds); + return ret; + } + + ret = kcm_ccache_retrieve_cred(context, ccache, flags, + &mcreds, &credp); + if (ret && ((flags & KRB5_GC_CACHED) == 0)) { + krb5_ccache_data ccdata; + krb5_creds in; + + memset(&in, 0, sizeof(in)); + + /* try and acquire */ + HEIMDAL_MUTEX_lock(&ccache->mutex); + + /* Fake up an internal ccache */ + kcm_internal_ccache(context, ccache, &ccdata); + + /* glue cc layer will store creds */ + ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp); + if (ret == 0) + free_creds = 1; + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + } + + if (ret == 0) { + ret = krb5_store_creds(response, credp); + } + + free(name); + krb5_free_creds_contents(context, &mcreds); + kcm_release_ccache(context, &ccache); + + if (free_creds) + krb5_free_cred_contents(context, credp); + + return ret; +} + +/* + * Request: + * NameZ + * + * Response: + * Principal + */ +static krb5_error_code +kcm_op_get_principal(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + if (ccache->client == NULL) + ret = KRB5_CC_NOTFOUND; + else + ret = krb5_store_principal(response, ccache->client); + + free(name); + kcm_release_ccache(context, &ccache); + + return 0; +} + +/* + * Request: + * NameZ + * + * Response: + * Cursor + * + */ +static krb5_error_code +kcm_op_get_first(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + u_int32_t cursor; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + ret = kcm_cursor_new(context, client->pid, ccache, &cursor); + if (ret) { + kcm_release_ccache(context, &ccache); + free(name); + return ret; + } + + ret = krb5_store_int32(response, cursor); + + free(name); + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * Cursor + * + * Response: + * Creds + */ +static krb5_error_code +kcm_op_get_next(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + char *name; + u_int32_t cursor; + kcm_cursor *c; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &cursor); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + ret = kcm_cursor_find(context, client->pid, ccache, cursor, &c); + if (ret) { + kcm_release_ccache(context, &ccache); + free(name); + return ret; + } + + HEIMDAL_MUTEX_lock(&ccache->mutex); + if (c->credp == NULL) { + ret = KRB5_CC_END; + } else { + ret = krb5_store_creds(response, &c->credp->cred); + c->credp = c->credp->next; + } + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + free(name); + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * Cursor + * + * Response: + * + */ +static krb5_error_code +kcm_op_end_get(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + u_int32_t cursor; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &cursor); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + ret = kcm_cursor_delete(context, client->pid, ccache, cursor); + + free(name); + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * WhichFields + * MatchCreds + * + * Response: + * + */ +static krb5_error_code +kcm_op_remove_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + u_int32_t whichfields; + krb5_creds mcreds; + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &whichfields); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_creds_tag(request, &mcreds); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + krb5_free_creds_contents(context, &mcreds); + return ret; + } + + ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds); + + /* XXX need to remove any events that match */ + + free(name); + krb5_free_creds_contents(context, &mcreds); + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * Flags + * + * Response: + * + */ +static krb5_error_code +kcm_op_set_flags(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + u_int32_t flags; + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &flags); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + /* we don't really support any flags yet */ + free(name); + kcm_release_ccache(context, &ccache); + + return 0; +} + +/* + * Request: + * NameZ + * UID + * GID + * + * Response: + * + */ +static krb5_error_code +kcm_op_chown(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + u_int32_t uid; + u_int32_t gid; + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &uid); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_int32(request, &gid); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + ret = kcm_chown(context, client, ccache, uid, gid); + + free(name); + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * Mode + * + * Response: + * + */ +static krb5_error_code +kcm_op_chmod(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + u_int16_t mode; + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int16(request, &mode); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + return ret; + } + + ret = kcm_chmod(context, client, ccache, mode); + + free(name); + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Protocol extensions for moving ticket acquisition responsibility + * from client to KCM follow. + */ + +/* + * Request: + * NameZ + * ServerPrincipalPresent + * ServerPrincipal OPTIONAL + * Key + * + * Repsonse: + * + */ +static krb5_error_code +kcm_op_get_initial_ticket(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + char *name; + int8_t not_tgt = 0; + krb5_principal server = NULL; + krb5_keyblock key; + + krb5_keyblock_zero(&key); + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int8(request, ¬_tgt); + if (ret) { + free(name); + return ret; + } + + if (not_tgt) { + ret = krb5_ret_principal(request, &server); + if (ret) { + free(name); + return ret; + } + } + + ret = krb5_ret_keyblock(request, &key); + if (ret) { + free(name); + if (server != NULL) + krb5_free_principal(context, server); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret == 0) { + HEIMDAL_MUTEX_lock(&ccache->mutex); + + if (ccache->server != NULL) { + krb5_free_principal(context, ccache->server); + ccache->server = NULL; + } + + krb5_free_keyblock(context, &ccache->key.keyblock); + + ccache->server = server; + ccache->key.keyblock = key; + ccache->flags |= KCM_FLAGS_USE_CACHED_KEY; + + ret = kcm_ccache_enqueue_default(context, ccache, NULL); + if (ret) { + ccache->server = NULL; + krb5_keyblock_zero(&ccache->key.keyblock); + ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY); + } + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + } + + free(name); + + if (ret != 0) { + krb5_free_principal(context, server); + krb5_free_keyblock(context, &key); + } + + kcm_release_ccache(context, &ccache); + + return ret; +} + +/* + * Request: + * NameZ + * ServerPrincipal + * KDCFlags + * EncryptionType + * + * Repsonse: + * + */ +static krb5_error_code +kcm_op_get_ticket(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + char *name; + krb5_principal server = NULL; + krb5_ccache_data ccdata; + krb5_creds in, *out; + krb5_kdc_flags flags; + + memset(&in, 0, sizeof(in)); + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &flags.i); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_int32(request, &in.session.keytype); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_principal(request, &server); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + krb5_free_principal(context, server); + free(name); + return ret; + } + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + /* Fake up an internal ccache */ + kcm_internal_ccache(context, ccache, &ccdata); + + in.client = ccache->client; + in.server = server; + in.times.endtime = 0; + + /* glue cc layer will store creds */ + ret = krb5_get_credentials_with_flags(context, 0, flags, + &ccdata, &in, &out); + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + if (ret == 0) + krb5_free_cred_contents(context, out); + + free(name); + + return ret; +} + +static const char *operations[KCM_OP_MAX] = { + "NOOP", + "GET_NAME", + "RESOLVE", + "GEN_NEW", + "INITIALIZE", + "DESTROY", + "STORE", + "RETRIEVE", + "GET_PRINCIPAL", + "GET_FIRST", + "GET_NEXT", + "END_GET", + "REMOVE_CRED", + "SET_FLAGS", + "CHOWN", + "CHMOD", + "GET_INITIAL_TICKET", + "GET_TICKET" +}; + +const char *kcm_op2string(kcm_operation operation) +{ + if (operation >= KCM_OP_MAX) + return "Unknown operation"; + + return operations[operation]; +} + +static kcm_method methods[KCM_OP_MAX] = { + kcm_op_noop, + kcm_op_get_name, + kcm_op_noop, /* kcm_op_resolve */ + kcm_op_gen_new, + kcm_op_initialize, + kcm_op_destroy, + kcm_op_store, + kcm_op_retrieve, + kcm_op_get_principal, + kcm_op_get_first, + kcm_op_get_next, + kcm_op_end_get, + kcm_op_remove_cred, + kcm_op_set_flags, + kcm_op_chown, + kcm_op_chmod, + kcm_op_get_initial_ticket, + kcm_op_get_ticket, +}; + +krb5_error_code +kcm_dispatch(krb5_context context, + kcm_client *client, + krb5_data *req_data, + krb5_data *resp_data) +{ + krb5_error_code ret; + kcm_method method; + krb5_storage *req_sp = NULL; + krb5_storage *resp_sp = NULL; + u_int16_t opcode; + + resp_sp = krb5_storage_emem(); + if (resp_sp == NULL) { + return ENOMEM; + } + + if (client->pid == 0) { + kcm_log(0, "Client had invalid process number"); + ret = KRB5_FCC_INTERNAL; + goto out; + } + + req_sp = krb5_storage_from_data(req_data); + if (req_sp == NULL) { + kcm_log(0, "Process %d: failed to initialize storage from data", + client->pid); + ret = KRB5_CC_IO; + goto out; + } + + krb5_ret_int16(req_sp, &opcode); + + if (opcode >= KCM_OP_MAX) { + kcm_log(0, "Process %d: invalid operation code %d", + client->pid, opcode); + ret = KRB5_FCC_INTERNAL; + goto out; + } + method = methods[opcode]; + + /* seek past place for status code */ + krb5_storage_seek(resp_sp, 4, SEEK_SET); + + ret = (*method)(context, client, opcode, req_sp, resp_sp); + +out: + if (req_sp != NULL) { + krb5_storage_free(req_sp); + } + + krb5_storage_seek(resp_sp, 0, SEEK_SET); + krb5_store_int32(resp_sp, ret); + + ret = krb5_storage_to_data(resp_sp, resp_data); + krb5_storage_free(resp_sp); + + return ret; +} + diff --git a/kcm/renew.c b/kcm/renew.c new file mode 100644 index 000000000..ea06208f3 --- /dev/null +++ b/kcm/renew.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kcm_locl.h" + +RCSID("$Id$"); + +krb5_error_code +kcm_ccache_refresh(krb5_context context, + kcm_ccache ccache, + krb5_creds **credp) +{ + krb5_error_code ret; + krb5_creds in, *out; + krb5_kdc_flags flags; + krb5_const_realm realm; + krb5_ccache_data ccdata; + + memset(&in, 0, sizeof(in)); + + KCM_ASSERT_VALID(ccache); + + if (ccache->client == NULL) { + /* no primary principal */ + kcm_log(0, "Refresh credentials requested but no client principal"); + return KRB5_CC_NOTFOUND; + } + + HEIMDAL_MUTEX_lock(&ccache->mutex); + + /* Fake up an internal ccache */ + kcm_internal_ccache(context, ccache, &ccdata); + + /* Find principal */ + in.client = ccache->client; + + if (ccache->server != NULL) { + ret = krb5_copy_principal(context, ccache->server, &in.server); + if (ret) { + kcm_log(0, "Failed to copy service principal: %s", + krb5_get_err_text(context, ret)); + goto out; + } + } else { + realm = krb5_principal_get_realm(context, in.client); + ret = krb5_make_principal(context, &in.server, realm, + KRB5_TGS_NAME, realm, NULL); + if (ret) { + kcm_log(0, "Failed to make TGS principal for realm %s: %s", + realm, krb5_get_err_text(context, ret)); + goto out; + } + } + + if (ccache->tkt_life) + in.times.endtime = time(NULL) + ccache->tkt_life; + if (ccache->renew_life) + in.times.renew_till = time(NULL) + ccache->renew_life; + + flags.i = 0; + flags.b.renewable = TRUE; + flags.b.renew = TRUE; + + ret = krb5_get_kdc_cred(context, + &ccdata, + flags, + NULL, + NULL, + &in, + &out); + if (ret) { + kcm_log(0, "Failed to renew credentials for cache %s: %s", + ccache->name, krb5_get_err_text(context, ret)); + goto out; + } + + /* Swap them in */ + kcm_ccache_remove_creds_internal(context, ccache); + + ret = kcm_ccache_store_cred_internal(context, ccache, out, 0, credp); + if (ret) { + kcm_log(0, "Failed to store credentials for cache %s: %s", + ccache->name, krb5_get_err_text(context, ret)); + krb5_free_creds(context, out); + goto out; + } + + free(out); /* but not contents */ + +out: + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + -- 2.11.4.GIT