From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- third_party/heimdal/kcm/Makefile.am | 45 + third_party/heimdal/kcm/NTMakefile | 35 + third_party/heimdal/kcm/acl.c | 201 ++++ third_party/heimdal/kcm/acquire.c | 149 +++ third_party/heimdal/kcm/cache.c | 678 +++++++++++++ third_party/heimdal/kcm/client.c | 223 +++++ third_party/heimdal/kcm/config.c | 423 ++++++++ third_party/heimdal/kcm/connect.c | 84 ++ third_party/heimdal/kcm/events.c | 444 +++++++++ third_party/heimdal/kcm/glue.c | 303 ++++++ third_party/heimdal/kcm/headers.h | 90 ++ third_party/heimdal/kcm/kcm.8 | 168 ++++ third_party/heimdal/kcm/kcm_locl.h | 186 ++++ third_party/heimdal/kcm/log.c | 85 ++ third_party/heimdal/kcm/main.c | 123 +++ third_party/heimdal/kcm/protocol.c | 1812 +++++++++++++++++++++++++++++++++++ third_party/heimdal/kcm/renew.c | 133 +++ third_party/heimdal/kcm/sessions.c | 83 ++ 18 files changed, 5265 insertions(+) create mode 100644 third_party/heimdal/kcm/Makefile.am create mode 100644 third_party/heimdal/kcm/NTMakefile create mode 100644 third_party/heimdal/kcm/acl.c create mode 100644 third_party/heimdal/kcm/acquire.c create mode 100644 third_party/heimdal/kcm/cache.c create mode 100644 third_party/heimdal/kcm/client.c create mode 100644 third_party/heimdal/kcm/config.c create mode 100644 third_party/heimdal/kcm/connect.c create mode 100644 third_party/heimdal/kcm/events.c create mode 100644 third_party/heimdal/kcm/glue.c create mode 100644 third_party/heimdal/kcm/headers.h create mode 100644 third_party/heimdal/kcm/kcm.8 create mode 100644 third_party/heimdal/kcm/kcm_locl.h create mode 100644 third_party/heimdal/kcm/log.c create mode 100644 third_party/heimdal/kcm/main.c create mode 100644 third_party/heimdal/kcm/protocol.c create mode 100644 third_party/heimdal/kcm/renew.c create mode 100644 third_party/heimdal/kcm/sessions.c (limited to 'third_party/heimdal/kcm') diff --git a/third_party/heimdal/kcm/Makefile.am b/third_party/heimdal/kcm/Makefile.am new file mode 100644 index 0000000..b9649d3 --- /dev/null +++ b/third_party/heimdal/kcm/Makefile.am @@ -0,0 +1,45 @@ +# $Id$ + +include $(top_srcdir)/Makefile.am.common + +AM_CPPFLAGS += $(INCLUDE_libintl) -I$(srcdir)/../lib/krb5 + +libexec_PROGRAMS = kcm + +kcm_SOURCES = \ + acl.c \ + acquire.c \ + cache.c \ + client.c \ + config.c \ + connect.c \ + events.c \ + glue.c \ + headers.h \ + kcm_locl.h \ + log.c \ + main.c \ + protocol.c \ + sessions.c \ + renew.c + +noinst_HEADERS = $(srcdir)/kcm-protos.h + +$(srcdir)/kcm-protos.h: $(kcm_SOURCES) + cd $(srcdir); perl ../cf/make-proto.pl -o kcm-protos.h -q -P comment $(kcm_SOURCES) || rm -f kcm-protos.h + +$(kcm_OBJECTS): $(srcdir)/kcm-protos.h + +man_MANS = kcm.8 + +LDADD = $(top_builddir)/lib/hdb/libhdb.la \ + $(top_builddir)/lib/krb5/libkrb5.la \ + $(LIB_hcrypto) \ + $(top_builddir)/lib/asn1/libasn1.la \ + $(top_builddir)/lib/ntlm/libheimntlm.la \ + $(top_builddir)/lib/ipc/libheim-ipcs.la \ + $(LIB_roken) \ + $(LIB_door_create) \ + $(LIB_pidfile) + +EXTRA_DIST = NTMakefile $(man_MANS) diff --git a/third_party/heimdal/kcm/NTMakefile b/third_party/heimdal/kcm/NTMakefile new file mode 100644 index 0000000..4f25946 --- /dev/null +++ b/third_party/heimdal/kcm/NTMakefile @@ -0,0 +1,35 @@ +######################################################################## +# +# Copyright (c) 2009, Secure Endpoints Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +# COPYRIGHT HOLDER 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. +# + +RELDIR=kcm + +!include ../windows/NTMakefile.w32 + diff --git a/third_party/heimdal/kcm/acl.c b/third_party/heimdal/kcm/acl.c new file mode 100644 index 0000000..5102c13 --- /dev/null +++ b/third_party/heimdal/kcm/acl.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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" + +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; + uint16_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: + case KCM_OP_MOVE_CACHE: + case KCM_OP_SET_DEFAULT_CACHE: + case KCM_OP_SET_KDC_OFFSET: + 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_CRED_UUID_LIST: + case KCM_OP_GET_CRED_BY_UUID: + case KCM_OP_GET_CACHE_UUID_LIST: + case KCM_OP_GET_CACHE_BY_UUID: + case KCM_OP_GET_DEFAULT_CACHE: + case KCM_OP_GET_KDC_OFFSET: + write_p = 0; + read_p = 1; + break; + default: + ret = KRB5_FCC_PERM; + goto out; + } + + 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 || + opcode == KCM_OP_MOVE_CACHE) { + ret = KRB5_FCC_PERM; + goto out; + } + + /* Let root always read system caches */ + if (CLIENT_IS_ROOT(client)) { + ret = 0; + goto out; + } + } + + /* start out with "other" mask */ + mask = S_IROTH|S_IWOTH; + + /* root can do anything */ + if (CLIENT_IS_ROOT(client)) { + if (read_p) + mask |= S_IRUSR|S_IRGRP|S_IROTH; + if (write_p) + mask |= S_IWUSR|S_IWGRP|S_IWOTH; + } + /* same session same as owner */ + if (kcm_is_same_session(client, ccache->uid, ccache->session)) { + if (read_p) + mask |= S_IROTH; + if (write_p) + mask |= S_IWOTH; + } + /* owner */ + if (client->uid == ccache->uid) { + if (read_p) + mask |= S_IRUSR; + if (write_p) + mask |= S_IWUSR; + } + /* group */ + if (client->gid == ccache->gid) { + if (read_p) + mask |= S_IRGRP; + if (write_p) + mask |= S_IWGRP; + } + + ret = (ccache->mode & 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, + uint16_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/third_party/heimdal/kcm/acquire.c b/third_party/heimdal/kcm/acquire.c new file mode 100644 index 0000000..a5450c0 --- /dev/null +++ b/third_party/heimdal/kcm/acquire.c @@ -0,0 +1,149 @@ +/* + * 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" + +/* + * 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 = 0; + krb5_creds cred; + krb5_const_realm realm; + krb5_get_init_creds_opt *opt = NULL; + krb5_ccache_data ccdata; + char *in_tkt_service = NULL; + const char *estr; + + *credp = 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) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to unparse service principal name for cache %s: %s", + ccache->name, estr); + krb5_free_error_message(context, estr); + goto out; + } + } + + realm = krb5_principal_get_realm(context, ccache->client); + + ret = krb5_get_init_creds_opt_alloc(context, &opt); + if (ret) + goto out; + 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 { + /* loosely based on lib/krb5/init_creds_pw.c */ + ret = krb5_get_init_creds_keytab(context, + &cred, + ccache->client, + ccache->key.keytab, + 0, + in_tkt_service, + opt); + } + + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to acquire credentials for cache %s: %s", + ccache->name, estr); + krb5_free_error_message(context, estr); + goto out; + } + + /* Swap them in */ + kcm_ccache_remove_creds_internal(context, ccache); + + ret = kcm_ccache_store_cred_internal(context, ccache, &cred, 0, credp); + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to store credentials for cache %s: %s", + ccache->name, estr); + krb5_free_error_message(context, estr); + krb5_free_cred_contents(context, &cred); + goto out; + } + +out: + free(in_tkt_service); + if (opt) + krb5_get_init_creds_opt_free(context, opt); + + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} diff --git a/third_party/heimdal/kcm/cache.c b/third_party/heimdal/kcm/cache.c new file mode 100644 index 0000000..b118127 --- /dev/null +++ b/third_party/heimdal/kcm/cache.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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" + +HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER; +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; + int ret; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + n = ++ccache_nextid; + HEIMDAL_MUTEX_unlock(&ccache_mutex); + + ret = asprintf(&name, "%ld:%u", (long)uid, n); + if (ret == -1) + return NULL; + + return name; +} + +krb5_error_code +kcm_ccache_resolve(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_ccache_resolve_by_uuid(krb5_context context, + kcmuuid_t uuid, + 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 (memcmp(p->uuid, uuid, sizeof(kcmuuid_t)) == 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_ccache_get_uuids(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *sp) +{ + krb5_error_code ret; + kcm_ccache p; + + 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; + ret = kcm_access(context, client, opcode, p); + if (ret) { + ret = 0; + continue; + } + krb5_storage_write(sp, p->uuid, sizeof(p->uuid)); + } + + 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) + (void) krb5_unparse_name(context, p->client, &cpn); + if (p->server != NULL) + (void) 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); + + free(cpn); + free(spn); + } + + return 0; +} + +static void +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; + cache->session = -1; + + kcm_zero_ccache_data_internal(context, cache); + + cache->tkt_life = 0; + cache->renew_life = 0; + cache->kdc_offset = 0; + + cache->next = NULL; + cache->refcnt = 0; + + HEIMDAL_MUTEX_unlock(&cache->mutex); + HEIMDAL_MUTEX_destroy(&cache->mutex); +} + + +krb5_error_code +kcm_ccache_destroy(krb5_context context, const char *name) +{ + kcm_ccache *p, ccache; + 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; + + if ((*p)->refcnt != 1) { + ret = EAGAIN; + goto out; + } + + ccache = *p; + *p = (*p)->next; + kcm_free_ccache_data_internal(context, ccache); + free(ccache); + +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; + + /* + * Create an enpty slot for us. + */ + 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; + } + + RAND_bytes(slot->uuid, sizeof(slot->uuid)); + + 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->key.keytab = NULL; + slot->tkt_life = 0; + slot->renew_life = 0; + slot->kdc_offset = 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; + + 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; + + 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; +} + +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 c) +{ + krb5_error_code ret = 0; + + KCM_ASSERT_VALID(c); + + HEIMDAL_MUTEX_lock(&c->mutex); + if (c->refcnt == 1) { + kcm_free_ccache_data_internal(context, c); + free(c); + } else { + c->refcnt--; + HEIMDAL_MUTEX_unlock(&c->mutex); + } + + 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_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(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; +} + +struct kcm_creds * +kcm_ccache_find_cred_uuid(krb5_context context, + kcm_ccache ccache, + kcmuuid_t uuid) +{ + struct kcm_creds *c; + + for (c = ccache->creds; c != NULL; c = c->next) + if (memcmp(c->uuid, uuid, sizeof(c->uuid)) == 0) + return c; + + return NULL; +} + + + +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 *)calloc(1, sizeof(**c)); + if (*c == NULL) + return KRB5_CC_NOMEM; + + RAND_bytes((*c)->uuid, sizeof((*c)->uuid)); + + *credp = &(*c)->cred; + + if (copy) { + ret = krb5_copy_creds_contents(context, creds, *credp); + if (ret) { + free(*c); + *c = NULL; + } + } else { + **credp = *creds; + ret = 0; + } + + return ret; +} + +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)) { + struct kcm_creds *cred = *c; + + *c = cred->next; + krb5_free_cred_contents(context, &cred->cred); + free(cred); + ret = 0; + if (*c == NULL) + break; + } + } + + 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; +} + +char * +kcm_ccache_first_name(kcm_client *client) +{ + kcm_ccache p; + char *name = NULL; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + + for (p = ccache_head; p != NULL; p = p->next) { + if (kcm_is_same_session(client, p->uid, p->session)) + break; + } + if (p) + name = strdup(p->name); + HEIMDAL_MUTEX_unlock(&ccache_mutex); + return name; +} diff --git a/third_party/heimdal/kcm/client.c b/third_party/heimdal/kcm/client.c new file mode 100644 index 0000000..061d4e9 --- /dev/null +++ b/third_party/heimdal/kcm/client.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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 + +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; + const char *estr; + + ret = kcm_ccache_resolve(context, name, ccache); + if (ret) { + char *uid = NULL; + + /* + * Both MIT and Heimdal are unable to, in krb5_cc_default(), call to + * KCM (or CCAPI, or LSA, or...) to get the user's default ccache name + * in their collection. Instead, the default ccache name is obtained + * in a static way, and for KCM that's "%{UID}". When we + * krb5_cc_switch(), we simply maintain a pointer to the name of the + * ccache that was made the default, but klist can't make use of this + * because krb5_cc_default() can't. + * + * The solution here is to first try resolving the ccache name given by + * the client, and if that fails but the name happens to be what would + * be the library's default KCM ccache name for that user, then try + * resolving it through the default ccache name pointer saved at switch + * time. + */ + if (asprintf(&uid, "%llu", (unsigned long long)client->uid) == -1 || + uid == NULL) + return ENOMEM; + + if (strcmp(name, uid) == 0) { + struct kcm_default_cache *c; + + for (c = default_caches; c != NULL; c = c->next) { + if (kcm_is_same_session(client, c->uid, c->session)) { + if (strcmp(c->name, name) != 0) { + ret = kcm_ccache_resolve(context, c->name, ccache); + break; + } + } + } + } + free(uid); + } + + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(1, "Failed to resolve cache %s: %s", name, estr); + krb5_free_error_message(context, estr); + 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; + const char *estr; + + ret = kcm_ccache_resolve(context, name, &ccache); + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(1, "Failed to resolve cache %s: %s", name, estr); + krb5_free_error_message(context, estr); + return ret; + } + + ret = kcm_access(context, client, KCM_OP_DESTROY, ccache); + kcm_cleanup_events(context, ccache); + kcm_release_ccache(context, ccache); + if (ret) + return ret; + + return kcm_ccache_destroy(context, name); +} + +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; + const char *estr; + + /* 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), "%ld:", (long)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; + } + + /* Allow root to create badly-named ccaches */ + if (bad && !CLIENT_IS_ROOT(client)) + return KRB5_CC_BADNAME; + } + + ret = kcm_ccache_resolve(context, name, &ccache); + if (ret == 0) { + if ((ccache->uid != client->uid || + ccache->gid != client->gid) && !CLIENT_IS_ROOT(client)) + return KRB5_FCC_PERM; + } else if (ret != KRB5_FCC_NOFILE && !(CLIENT_IS_ROOT(client) && ret == KRB5_FCC_PERM)) { + return ret; + } + + if (ret == KRB5_FCC_NOFILE) { + ret = kcm_ccache_new(context, name, &ccache); + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(1, "Failed to initialize cache %s: %s", name, estr); + krb5_free_error_message(context, estr); + return ret; + } + + /* bind to current client */ + ccache->uid = client->uid; + ccache->gid = client->gid; + ccache->session = client->session; + } else { + ret = kcm_zero_ccache_data(context, ccache); + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(1, "Failed to empty cache %s: %s", name, estr); + krb5_free_error_message(context, estr); + 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; + } + + /* + * Finally, if the user is root and the cache was created under + * another user's name, chown the cache to that user and their + * default gid. + */ + if (CLIENT_IS_ROOT(client)) { + unsigned long uid; + int matches = sscanf(name,"%ld:",&uid); + if (matches == 0) + matches = sscanf(name,"%ld",&uid); + if (matches == 1) { + struct passwd *pwd = getpwuid(uid); + if (pwd != NULL) { + gid_t gid = pwd->pw_gid; + kcm_chown(context, client, ccache, uid, gid); + } + } + } + + *ccache_p = ccache; + return 0; +} + diff --git a/third_party/heimdal/kcm/config.c b/third_party/heimdal/kcm/config.c new file mode 100644 index 0000000..217d28d --- /dev/null +++ b/third_party/heimdal/kcm/config.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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 + +#define MAX_REQUEST_MAX 67108864ll /* 64MB, the maximum accepted value of max_request */ + +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 */ + +int detach_from_console = -1; +int daemon_child = -1; +int automatic_renewal = -1; + +static const char *system_cache_name = NULL; +static const char *system_keytab = NULL; +static const char *system_principal = NULL; +static const char *system_server = NULL; +static const char *system_perms = NULL; +static const char *system_user = NULL; +static const char *system_group = NULL; + +static const char *renew_life = NULL; +static const char *ticket_life = NULL; + +int launchd_flag = 0; +int disallow_getting_krbtgt = 0; +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" + }, + { + "launchd", 0, arg_flag, &launchd_flag, + "when in use by launchd", NULL + }, + { + "detach", 0 , arg_flag, &detach_from_console, + "detach from console", NULL + }, + { + "daemon-child", 0 , arg_integer, &daemon_child, + "private argument, do not use", NULL + }, + { + "automatic-renewal", 0 , arg_negative_flag, &automatic_renewal, + "disable automatic TGT renewal", NULL + }, + { "help", 'h', arg_flag, &help_flag, NULL, NULL }, + { + "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", NULL + }, + { + "disallow-getting-krbtgt", 0, arg_flag, &disallow_getting_krbtgt, + "disable fetching krbtgt from the cache", NULL + }, + { + "renewable-life", 'r', arg_string, &renew_life, + "renewable lifetime of system tickets", "time" + }, + { + "max-request", 'r', arg_integer, &max_request_str, + "max request size", "bytes" + }, + { + "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, NULL, NULL } +}; + +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((unsigned char)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((unsigned char)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 const char * +kcm_system_config_get_string(const char *string) +{ + return krb5_config_get_string(kcm_context, NULL, "kcm", + "system_ccache", string, NULL); +} + +static krb5_error_code +ccache_init_system(void) +{ + kcm_ccache ccache; + krb5_error_code ret; + + if (system_cache_name == NULL) + system_cache_name = kcm_system_config_get_string("cc_name"); + + 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) + system_server = kcm_system_config_get_string("server"); + + 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) + system_keytab = kcm_system_config_get_string("keytab_name"); + + 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 = kcm_system_config_get_string("renew_life"); + + if (renew_life == NULL) + renew_life = "6 months"; + + 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) + ticket_life = kcm_system_config_get_string("ticket_life"); + + 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) + system_perms = kcm_system_config_get_string("mode"); + + if (system_perms != NULL) { + int mode; + + if (sscanf(system_perms, "%o", &mode) != 1) + return EINVAL; + + ccache->mode = mode; + } + + if (disallow_getting_krbtgt == -1) { + disallow_getting_krbtgt = + krb5_config_get_bool_default(kcm_context, NULL, FALSE, "kcm", + "disallow-getting-krbtgt", NULL); + } + + /* 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 optidx = 0; + const char *p; + + while (getarg(args, num_args, argc, argv, &optidx)) + warnx("error at argument `%s'", argv[optidx]); + + if (help_flag) + usage (0); + + if (version_flag) { + print_version(NULL); + exit(0); + } + + argc -= optidx; + argv += optidx; + + 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) { + int64_t bytes; + + if ((bytes = parse_bytes(max_request_str, NULL)) < 0) + krb5_errx(kcm_context, 1, + "--max-request size must be non-negative"); + if (bytes > MAX_REQUEST_MAX) + krb5_errx(kcm_context, 1, "--max-request size is too big " + "(must be smaller than %lld)", MAX_REQUEST_MAX); + + max_request = bytes; + } + + if(max_request == 0){ + p = krb5_config_get_string (kcm_context, + NULL, + "kcm", + "max-request", + NULL); + if (p) { + int64_t bytes; + + if ((bytes = parse_bytes(max_request_str, NULL)) < 0) + krb5_errx(kcm_context, 1, + "[kcm] max-request size must be non-negative"); + if (bytes > MAX_REQUEST_MAX) + krb5_errx(kcm_context, 1, "[kcm] max-request size is too big " + "(must be smaller than %lld)", MAX_REQUEST_MAX); + + max_request = bytes; + } + } + + if (system_principal == NULL) { + system_principal = kcm_system_config_get_string("principal"); + } + + if (system_principal != NULL) { + ret = ccache_init_system(); + if (ret) + krb5_err(kcm_context, 1, ret, "initializing system ccache"); + } + + if(automatic_renewal == -1) + automatic_renewal = krb5_config_get_bool_default(kcm_context, NULL, + TRUE, + "kcm", + "automatic_renewal", + NULL); + + if(detach_from_console == -1) + detach_from_console = krb5_config_get_bool_default(kcm_context, NULL, + FALSE, + "kcm", + "detach", NULL); + kcm_openlog(); + if(max_request == 0) + max_request = 64 * 1024; +} + diff --git a/third_party/heimdal/kcm/connect.c b/third_party/heimdal/kcm/connect.c new file mode 100644 index 0000000..ee09193 --- /dev/null +++ b/third_party/heimdal/kcm/connect.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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" + +void +kcm_service(void *ctx, const heim_idata *req, + const heim_icred cred, + heim_ipc_complete complete, + heim_sipc_call cctx) +{ + kcm_client peercred; + krb5_error_code ret; + krb5_data request, rep; + unsigned char *buf; + size_t len; + + krb5_data_zero(&rep); + + peercred.uid = heim_ipc_cred_get_uid(cred); + peercred.gid = heim_ipc_cred_get_gid(cred); + peercred.pid = heim_ipc_cred_get_pid(cred); + peercred.session = heim_ipc_cred_get_session(cred); + + if (req->length < 4) { + kcm_log(1, "malformed request from process %d (too short)", + peercred.pid); + (*complete)(cctx, EINVAL, NULL); + return; + } + + buf = req->data; + len = req->length; + + 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], peercred.pid); + (*complete)(cctx, EINVAL, NULL); + return; + } + + request.data = buf + 2; + request.length = len - 2; + + /* buf is now pointing at opcode */ + + ret = kcm_dispatch(kcm_context, &peercred, &request, &rep); + + (*complete)(cctx, ret, &rep); + krb5_data_free(&rep); +} diff --git a/third_party/heimdal/kcm/events.c b/third_party/heimdal/kcm/events.c new file mode 100644 index 0000000..8b78c10 --- /dev/null +++ b/third_party/heimdal/kcm/events.c @@ -0,0 +1,444 @@ +/* + * 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 t, char buf[64]) +{ + if (t) + strftime(buf, 64, "%m-%dT%H:%M", gmtime(&t)); + 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 (automatic_renewal && 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); + if (ret == KRB5KRB_AP_ERR_TKT_EXPIRED) { + ret = kcm_ccache_acquire(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; + const char *estr; + + 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) { + estr = krb5_get_error_message(context, ret); + kcm_log(1, "Could not fire event for cache %s: %s", + (*e)->ccache->name, estr); + krb5_free_error_message(context, estr); + } + } else if ((*e)->expire_time && now >= (*e)->expire_time) { + ret = kcm_remove_event_internal(context, e); + if (ret) { + estr = krb5_get_error_message(context, ret); + kcm_log(1, "Could not expire event for cache %s: %s", + (*e)->ccache->name, estr); + krb5_free_error_message(context, estr); + } + } + + if (*e == NULL) + break; + } + + last_run = now; + + HEIMDAL_MUTEX_unlock(&events_mutex); + + return 0; +} + diff --git a/third_party/heimdal/kcm/glue.c b/third_party/heimdal/kcm/glue.c new file mode 100644 index 0000000..0895f48 --- /dev/null +++ b/third_party/heimdal/kcm/glue.c @@ -0,0 +1,303 @@ +/* + * 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 krb5_error_code +kcmss_get_name_2(krb5_context context, + krb5_ccache id, + const char **name, + const char **col, + const char **sub) +{ + if (name) + *name = CACHENAME(id); + if (col) + *col = NULL; + if (sub) + *sub = CACHENAME(id); + return 0; +} + +static krb5_error_code +kcmss_resolve_2(krb5_context context, + krb5_ccache *id, + const char *res, + const char *sub) +{ + 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) +{ + kcm_ccache c = KCMCACHE(id); + + KCM_ASSERT_VALID(c); + + id->data.data = NULL; + id->data.length = 0; + + return 0; +} + +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 = { + KRB5_CC_OPS_VERSION_5, + "KCM", + NULL, + NULL, + 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, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + kcmss_get_name_2, + kcmss_resolve_2, +}; + +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/third_party/heimdal/kcm/headers.h b/third_party/heimdal/kcm/headers.h new file mode 100644 index 0000000..603a6b8 --- /dev/null +++ b/third_party/heimdal/kcm/headers.h @@ -0,0 +1,90 @@ +/* + * 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__ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_PARAM_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_SYS_UCRED_H +#include +#endif +#ifdef HAVE_UTIL_H +#include +#endif +#ifdef HAVE_LIBUTIL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include + +#include "crypto-headers.h" + +#endif /* __HEADERS_H__ */ + diff --git a/third_party/heimdal/kcm/kcm.8 b/third_party/heimdal/kcm/kcm.8 new file mode 100644 index 0000000..744ab0f --- /dev/null +++ b/third_party/heimdal/kcm/kcm.8 @@ -0,0 +1,168 @@ +.\" Copyright (c) 2005 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. +.\" +.\" $Id$ +.\" +.Dd May 29, 2005 +.Dt KCM 8 +.Os +.Sh NAME +.Nm kcm +.Nd process-based credential cache for Kerberos tickets. +.Sh SYNOPSIS +.Nm +.Op Fl Fl cache-name= Ns Ar cachename +.Oo Fl c Ar file \*(Ba Xo +.Fl Fl config-file= Ns Ar file +.Xc +.Oc +.Oo Fl g Ar group \*(Ba Xo +.Fl Fl group= Ns Ar group +.Xc +.Oc +.Op Fl Fl max-request= Ns Ar size +.Op Fl Fl disallow-getting-krbtgt +.Op Fl Fl detach +.Op Fl h | Fl Fl help +.Oo Fl k Ar principal \*(Ba Xo +.Fl Fl system-principal= Ns Ar principal +.Xc +.Oc +.Oo Fl l Ar time \*(Ba Xo +.Fl Fl lifetime= Ns Ar time +.Xc +.Oc +.Oo Fl m Ar mode \*(Ba Xo +.Fl Fl mode= Ns Ar mode +.Xc +.Oc +.Op Fl n | Fl Fl no-name-constraints +.Oo Fl r Ar time \*(Ba Xo +.Fl Fl renewable-life= Ns Ar time +.Xc +.Oc +.Oo Fl s Ar path \*(Ba Xo +.Fl Fl socket-path= Ns Ar path +.Xc +.Oc +.Oo Fl S Ar principal \*(Ba Xo +.Fl Fl server= Ns Ar principal +.Xc +.Oc +.Oo Fl t Ar keytab \*(Ba Xo +.Fl Fl keytab= Ns Ar keytab +.Xc +.Oc +.Oo Fl u Ar user \*(Ba Xo +.Fl Fl user= Ns Ar user +.Xc +.Oc +.Op Fl v | Fl Fl version +.Sh DESCRIPTION +.Nm +is a process based credential cache. +To use it, set the +.Ev KRB5CCNAME +environment variable to +.Ql KCM: Ns Ar uid +or add the stanza +.Bd -literal + +[libdefaults] + default_cc_name = KCM:%{uid} + +.Ed +to the +.Pa /etc/krb5.conf +configuration file and make sure +.Nm kcm +is started in the system startup files. +.Pp +The +.Nm +daemon can hold the credentials for all users in the system. Access +control is done with Unix-like permissions. The daemon checks the +access on all operations based on the uid and gid of the user. The +tickets are renewed as long as is permitted by the KDC's policy. +.Pp +The +.Nm +daemon can also keep a SYSTEM credential that server processes can +use to access services. One example of usage might be an nss_ldap +module that quickly needs to get credentials and doesn't want to renew +the ticket itself. +.Pp +Supported options: +.Bl -tag -width Ds +.It Fl Fl cache-name= Ns Ar cachename +system cache name +.It Fl c Ar file , Fl Fl config-file= Ns Ar file +location of config file +.It Fl g Ar group , Fl Fl group= Ns Ar group +system cache group +.It Fl Fl max-request= Ns Ar size +max size for a kcm-request +.It Fl Fl disallow-getting-krbtgt +disallow extracting any krbtgt from the +.Nm kcm +daemon. +.It Fl Fl detach +detach from console +.It Fl h , Fl Fl help +.It Fl k Ar principal , Fl Fl system-principal= Ns Ar principal +system principal name +.It Fl l Ar time , Fl Fl lifetime= Ns Ar time +lifetime of system tickets +.It Fl m Ar mode , Fl Fl mode= Ns Ar mode +octal mode of system cache +.It Fl n , Fl Fl no-name-constraints +disable credentials cache name constraints +.It Fl r Ar time , Fl Fl renewable-life= Ns Ar time +renewable lifetime of system tickets +.It Fl s Ar path , Fl Fl socket-path= Ns Ar path +path to kcm domain socket +.It Fl S Ar principal , Fl Fl server= Ns Ar principal +server to get system ticket for +.It Fl t Ar keytab , Fl Fl keytab= Ns Ar keytab +system keytab name +.It Fl u Ar user , Fl Fl user= Ns Ar user +system cache owner +.It Fl v , Fl Fl version +.El +.\".Sh ENVIRONMENT +.\".Sh FILES +.\".Sh EXAMPLES +.\".Sh DIAGNOSTICS +.\".Sh SEE ALSO +.\".Sh STANDARDS +.\".Sh HISTORY +.\".Sh AUTHORS +.\".Sh BUGS diff --git a/third_party/heimdal/kcm/kcm_locl.h b/third_party/heimdal/kcm/kcm_locl.h new file mode 100644 index 0000000..dc01104 --- /dev/null +++ b/third_party/heimdal/kcm/kcm_locl.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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; + +struct kcm_default_cache { + uid_t uid; + pid_t session; /* really au_asid_t */ + char *name; + struct kcm_default_cache *next; +}; + +extern struct kcm_default_cache *default_caches; + +struct kcm_creds { + kcmuuid_t uuid; + krb5_creds cred; + struct kcm_creds *next; +}; + +typedef struct kcm_ccache_data { + char *name; + kcmuuid_t uuid; + unsigned refcnt; + uint16_t flags; + uint16_t mode; + uid_t uid; + gid_t gid; + pid_t session; /* really au_asid_t */ + krb5_principal client; /* primary client principal */ + krb5_principal server; /* primary server principal (TGS if NULL) */ + struct kcm_creds *creds; + krb5_deltat tkt_life; + krb5_deltat renew_life; + int32_t kdc_offset; + 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; + +/* 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) + + +/* 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; + pid_t session; +} kcm_client; + +#define CLIENT_IS_ROOT(client) ((client)->uid == 0) + +/* Dispatch table */ +/* passed in OPERATION | ... ; returns STATUS | ... */ +typedef krb5_error_code (*kcm_method)(krb5_context, kcm_client *, kcm_operation, krb5_storage *, krb5_storage *); + +struct kcm_op { + const char *name; + kcm_method method; +}; + +#define DEFAULT_LOG_DEST "0/FILE:" LOCALSTATEDIR "/log/kcmd.log" +#define _PATH_KCM_CONF SYSCONFDIR "/kcm.conf" + +extern krb5_context kcm_context; +extern size_t max_request; +extern sig_atomic_t exit_flag; +extern int name_constraints; +extern int detach_from_console; +extern int daemon_child; +extern int automatic_renewal; +extern int launchd_flag; +extern int disallow_getting_krbtgt; + +#if 0 +extern const krb5_cc_ops krb5_kcmss_ops; +#endif + +void kcm_service(void *, const heim_idata *, const heim_icred, + heim_ipc_complete, heim_sipc_call); + +#include + +#endif /* __KCM_LOCL_H__ */ + diff --git a/third_party/heimdal/kcm/log.c b/third_party/heimdal/kcm/log.c new file mode 100644 index 0000000..c77d1ff --- /dev/null +++ b/third_party/heimdal/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 *logfac; + +void +kcm_openlog(void) +{ + char **s = NULL, **p; + krb5_initlog(kcm_context, "kcm", &logfac); + 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, logfac, *p); + krb5_config_free_strings(s); + }else + krb5_addlog_dest(kcm_context, logfac, DEFAULT_LOG_DEST); + krb5_set_warn_dest(kcm_context, logfac); +} + +char* +kcm_log_msg_va(int level, const char *fmt, va_list ap) +{ + char *msg; + krb5_vlog_msg(kcm_context, logfac, &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/third_party/heimdal/kcm/main.c b/third_party/heimdal/kcm/main.c new file mode 100644 index 0000000..155983a --- /dev/null +++ b/third_party/heimdal/kcm/main.c @@ -0,0 +1,123 @@ +/* + * 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$"); + +krb5_context kcm_context = NULL; +extern const char *socket_path; + +const char *service_name = "org.h5l.kcm"; + +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 = sigusr1; + sigemptyset(&sa.sa_mask); + + sigaction(SIGUSR1, &sa, NULL); + + sa.sa_handler = sigusr2; + sigaction(SIGUSR2, &sa, NULL); + + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + } +#else + signal(SIGUSR1, sigusr1); + signal(SIGUSR2, sigusr2); + signal(SIGPIPE, SIG_IGN); +#endif + if (detach_from_console && !launchd_flag && daemon_child == -1) + daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); + rk_pidfile(NULL); + + if (socket_path) + setenv("HEIM_IPC_DIR", socket_path, 1); + + if (launchd_flag) { + heim_sipc mach; + ret = heim_sipc_launchd_mach_init(service_name, kcm_service, NULL, &mach); + if (ret) + krb5_err(kcm_context, 1, ret, "Could not setup launchd service"); + } else { + heim_sipc un; + ret = heim_sipc_service_unix(service_name, kcm_service, NULL, &un); + if (ret) + krb5_err(kcm_context, 1, ret, "Could not setup Unix domain socket service"); + } +#ifdef HAVE_DOOR_CREATE + { + heim_sipc door; + ret = heim_sipc_service_door(service_name, kcm_service, NULL, &door); + if (ret) + krb5_err(kcm_context, 1, ret, "Could not setup door service"); + } +#endif + + roken_detach_finish(NULL, daemon_child); + + heim_ipc_main(); + + krb5_free_context(kcm_context); + return 0; +} diff --git a/third_party/heimdal/kcm/protocol.c b/third_party/heimdal/kcm/protocol.c new file mode 100644 index 0000000..b5442e4 --- /dev/null +++ b/third_party/heimdal/kcm/protocol.c @@ -0,0 +1,1812 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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 + +static void +kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name); + + +int +kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session) +{ +#if 0 /* XXX pppd is running in diffrent session the user */ + if (session != -1) + return (client->session == session); + else +#endif + return (client->uid == uid); +} + +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); + if (ret == 0) + kcm_drop_default_cache(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_cred_contents(context, &creds); + return ret; + } + + ret = kcm_ccache_store_cred(context, ccache, &creds, 0); + if (ret) { + free(name); + krb5_free_cred_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) +{ + uint32_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_uint32(request, &flags); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_creds_tag(request, &mcreds); + if (ret) { + free(name); + return ret; + } + + if (disallow_getting_krbtgt && krb5_principal_is_krbtgt(context, mcreds.server)) + { + free(name); + krb5_free_cred_contents(context, &mcreds); + return KRB5_FCC_PERM; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + if (ret) { + free(name); + krb5_free_cred_contents(context, &mcreds); + return ret; + } + + ret = kcm_ccache_retrieve_cred(context, ccache, flags, + &mcreds, &credp); + if (ret && ((flags & KRB5_GC_CACHED) == 0) && + !krb5_is_config_principal(context, mcreds.server)) { + krb5_ccache_data ccdata; + + /* 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_cred_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 ret; +} + +/* + * Request: + * NameZ + * + * Response: + * UUIDs + * + */ +static krb5_error_code +kcm_op_get_cred_uuid_list(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_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 = kcm_ccache_resolve_client(context, client, opcode, + name, &ccache); + free(name); + if (ret) + return ret; + + for (creds = ccache->creds ; creds ; creds = creds->next) { + ssize_t sret; + sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid)); + if (sret != sizeof(creds->uuid)) { + ret = ENOMEM; + break; + } + } + + kcm_release_ccache(context, ccache); + + return ret; +} + +/* + * Request: + * NameZ + * Cursor + * + * Response: + * Creds + */ +static krb5_error_code +kcm_op_get_cred_by_uuid(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + char *name; + struct kcm_creds *c; + kcmuuid_t uuid; + ssize_t sret; + + 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); + free(name); + if (ret) + return ret; + + sret = krb5_storage_read(request, &uuid, sizeof(uuid)); + if (sret != sizeof(uuid)) { + kcm_release_ccache(context, ccache); + krb5_clear_error_message(context); + return KRB5_CC_IO; + } + + c = kcm_ccache_find_cred_uuid(context, ccache, uuid); + if (c == NULL) { + kcm_release_ccache(context, ccache); + return KRB5_CC_END; + } + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = krb5_store_creds(response, &c->cred); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + 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) +{ + uint32_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_uint32(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_cred_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_cred_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) +{ + uint32_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_uint32(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) +{ + uint32_t uid; + uint32_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_uint32(request, &uid); + if (ret) { + free(name); + return ret; + } + + ret = krb5_ret_uint32(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) +{ + uint16_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_uint16(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_contents(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_uint32(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); + + krb5_free_principal(context, server); + + if (ret == 0) + krb5_free_cred_contents(context, out); + + kcm_release_ccache(context, ccache); + free(name); + + return ret; +} + +/* + * Request: + * OldNameZ + * NewNameZ + * + * Repsonse: + * + */ +static krb5_error_code +kcm_op_move_cache(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache oldid, newid; + char *oldname, *newname; + + ret = krb5_ret_stringz(request, &oldname); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, oldname); + + ret = krb5_ret_stringz(request, &newname); + if (ret) { + free(oldname); + return ret; + } + + /* move to ourself is simple, done! */ + if (strcmp(oldname, newname) == 0) { + free(oldname); + free(newname); + return 0; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid); + if (ret) { + free(oldname); + free(newname); + return ret; + } + + /* Check if new credential cache exists, if not create one. */ + ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid); + if (ret == KRB5_FCC_NOFILE) + ret = kcm_ccache_new_client(context, client, newname, &newid); + free(newname); + + if (ret) { + free(oldname); + kcm_release_ccache(context, oldid); + return ret; + } + + HEIMDAL_MUTEX_lock(&oldid->mutex); + HEIMDAL_MUTEX_lock(&newid->mutex); + + /* move content */ + { + kcm_ccache_data tmp; + +#define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; } + + MOVE(newid, oldid, flags); + MOVE(newid, oldid, client); + MOVE(newid, oldid, server); + MOVE(newid, oldid, creds); + MOVE(newid, oldid, tkt_life); + MOVE(newid, oldid, renew_life); + MOVE(newid, oldid, key); + MOVE(newid, oldid, kdc_offset); +#undef MOVE + } + + HEIMDAL_MUTEX_unlock(&oldid->mutex); + HEIMDAL_MUTEX_unlock(&newid->mutex); + + kcm_release_ccache(context, oldid); + kcm_release_ccache(context, newid); + + ret = kcm_ccache_destroy_client(context, client, oldname); + if (ret == 0) + kcm_drop_default_cache(context, client, oldname); + + free(oldname); + + return ret; +} + +static krb5_error_code +kcm_op_get_cache_uuid_list(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + KCM_LOG_REQUEST(context, client, opcode); + + return kcm_ccache_get_uuids(context, client, opcode, response); +} + +static krb5_error_code +kcm_op_get_cache_by_uuid(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcmuuid_t uuid; + ssize_t sret; + kcm_ccache cache; + + KCM_LOG_REQUEST(context, client, opcode); + + sret = krb5_storage_read(request, &uuid, sizeof(uuid)); + if (sret != sizeof(uuid)) { + krb5_clear_error_message(context); + return KRB5_CC_IO; + } + + ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache); + if (ret) + return ret; + + ret = kcm_access(context, client, opcode, cache); + if (ret) + ret = KRB5_FCC_NOFILE; + + if (ret == 0) + ret = krb5_store_stringz(response, cache->name); + + kcm_release_ccache(context, cache); + + return ret; +} + +struct kcm_default_cache *default_caches; + +static krb5_error_code +kcm_op_get_default_cache(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_default_cache *c; + krb5_error_code ret; + const char *name = NULL; + char *n = NULL; + int aret; + + KCM_LOG_REQUEST(context, client, opcode); + + for (c = default_caches; c != NULL; c = c->next) { + if (kcm_is_same_session(client, c->uid, c->session)) { + name = c->name; + break; + } + } + if (name == NULL) + name = n = kcm_ccache_first_name(client); + + if (name == NULL) { + aret = asprintf(&n, "%d", (int)client->uid); + if (aret != -1) + name = n; + } + if (name == NULL) + return ENOMEM; + ret = krb5_store_stringz(response, name); + if (n) + free(n); + return ret; +} + +static void +kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name) +{ + struct kcm_default_cache **c; + + for (c = &default_caches; *c != NULL; c = &(*c)->next) { + if (!kcm_is_same_session(client, (*c)->uid, (*c)->session)) + continue; + if (strcmp((*c)->name, name) == 0) { + struct kcm_default_cache *h = *c; + *c = (*c)->next; + free(h->name); + free(h); + break; + } + } +} + +static krb5_error_code +kcm_op_set_default_cache(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_default_cache *c; + krb5_error_code ret; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + for (c = default_caches; c != NULL; c = c->next) { + if (kcm_is_same_session(client, c->uid, c->session)) + break; + } + if (c == NULL) { + c = malloc(sizeof(*c)); + if (c == NULL) { + free(name); + return ENOMEM; + } + c->session = client->session; + c->uid = client->uid; + c->name = name; + + c->next = default_caches; + default_caches = c; + } else { + free(c->name); + c->name = name; + } + + return 0; +} + +static krb5_error_code +kcm_op_get_kdc_offset(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); + free(name); + if (ret) + return ret; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = krb5_store_int32(response, ccache->kdc_offset); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + kcm_release_ccache(context, ccache); + + return ret; +} + +static krb5_error_code +kcm_op_set_kdc_offset(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + int32_t offset; + 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, &offset); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); + free(name); + if (ret) + return ret; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ccache->kdc_offset = offset; + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + kcm_release_ccache(context, ccache); + + return ret; +} + +struct kcm_ntlm_cred { + kcmuuid_t uuid; + char *user; + char *domain; + krb5_data nthash; + uid_t uid; + pid_t session; + struct kcm_ntlm_cred *next; +}; + +static struct kcm_ntlm_cred *ntlm_head; + +static void +free_cred(struct kcm_ntlm_cred *cred) +{ + free(cred->user); + free(cred->domain); + krb5_data_free(&cred->nthash); + free(cred); +} + + +/* + * name + * domain + * ntlm hash + * + * Reply: + * uuid + */ + +static struct kcm_ntlm_cred * +find_ntlm_cred(const char *user, const char *domain, kcm_client *client) +{ + struct kcm_ntlm_cred *c; + + for (c = ntlm_head; c != NULL; c = c->next) + if ((user[0] == '\0' || strcmp(user, c->user) == 0) && + (domain == NULL || strcmp(domain, c->domain) == 0) && + kcm_is_same_session(client, c->uid, c->session)) + return c; + + return NULL; +} + +static krb5_error_code +kcm_op_add_ntlm_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *cred, *c; + krb5_error_code ret; + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) + return ENOMEM; + + RAND_bytes(cred->uuid, sizeof(cred->uuid)); + + ret = krb5_ret_stringz(request, &cred->user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &cred->domain); + if (ret) + goto error; + + ret = krb5_ret_data(request, &cred->nthash); + if (ret) + goto error; + + /* search for dups */ + c = find_ntlm_cred(cred->user, cred->domain, client); + if (c) { + krb5_data hash = c->nthash; + c->nthash = cred->nthash; + cred->nthash = hash; + free_cred(cred); + cred = c; + } else { + cred->next = ntlm_head; + ntlm_head = cred; + } + + cred->uid = client->uid; + cred->session = client->session; + + /* write response */ + (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid)); + + return 0; + + error: + free_cred(cred); + + return ret; +} + +/* + * { "HAVE_NTLM_CRED", NULL }, + * + * input: + * name + * domain + */ + +static krb5_error_code +kcm_op_have_ntlm_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *c; + char *user = NULL, *domain = NULL; + krb5_error_code ret; + + ret = krb5_ret_stringz(request, &user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &domain); + if (ret) + goto error; + + if (domain[0] == '\0') { + free(domain); + domain = NULL; + } + + c = find_ntlm_cred(user, domain, client); + if (c == NULL) + ret = ENOENT; + + error: + free(user); + if (domain) + free(domain); + + return ret; +} + +/* + * { "DEL_NTLM_CRED", NULL }, + * + * input: + * name + * domain + */ + +static krb5_error_code +kcm_op_del_ntlm_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred **cp, *c; + char *user = NULL, *domain = NULL; + krb5_error_code ret; + + ret = krb5_ret_stringz(request, &user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &domain); + if (ret) + goto error; + + for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { + if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 && + kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) + { + c = *cp; + *cp = c->next; + + free_cred(c); + break; + } + } + + error: + free(user); + free(domain); + + return ret; +} + +/* + * { "DO_NTLM_AUTH", NULL }, + * + * input: + * name:string + * domain:string + * type2:data + * + * reply: + * type3:data + * flags:int32 + * session-key:data + */ + +#define NTLM_FLAG_SESSIONKEY 1 +#define NTLM_FLAG_NTLM2_SESSION 2 +#define NTLM_FLAG_KEYEX 4 + +static krb5_error_code +kcm_op_do_ntlm(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *c; + struct ntlm_type2 type2; + struct ntlm_type3 type3; + char *user = NULL, *domain = NULL; + struct ntlm_buf ndata, sessionkey; + krb5_data data; + krb5_error_code ret; + uint32_t flags = 0; + + memset(&type2, 0, sizeof(type2)); + memset(&type3, 0, sizeof(type3)); + sessionkey.data = NULL; + sessionkey.length = 0; + + ret = krb5_ret_stringz(request, &user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &domain); + if (ret) + goto error; + + if (domain[0] == '\0') { + free(domain); + domain = NULL; + } + + c = find_ntlm_cred(user, domain, client); + if (c == NULL) { + ret = EINVAL; + goto error; + } + + ret = krb5_ret_data(request, &data); + if (ret) + goto error; + + ndata.data = data.data; + ndata.length = data.length; + + ret = heim_ntlm_decode_type2(&ndata, &type2); + krb5_data_free(&data); + if (ret) + goto error; + + if (domain && strcmp(domain, type2.targetname) == 0) { + ret = EINVAL; + goto error; + } + + type3.username = c->user; + type3.flags = type2.flags; + type3.targetname = type2.targetname; + type3.ws = rk_UNCONST("workstation"); + + /* + * NTLM Version 1 if no targetinfo buffer. + */ + + if (1 || type2.targetinfo.length == 0) { + struct ntlm_buf tmpsesskey; + + if (type2.flags & NTLM_NEG_NTLM2_SESSION) { + unsigned char nonce[8]; + + if (RAND_bytes(nonce, sizeof(nonce)) != 1) { + ret = EINVAL; + goto error; + } + + ret = heim_ntlm_calculate_ntlm2_sess(nonce, + type2.challenge, + c->nthash.data, + &type3.lm, + &type3.ntlm); + } else { + ret = heim_ntlm_calculate_ntlm1(c->nthash.data, + c->nthash.length, + type2.challenge, + &type3.ntlm); + + } + if (ret) + goto error; + + ret = heim_ntlm_build_ntlm1_master(c->nthash.data, + c->nthash.length, + &tmpsesskey, + &type3.sessionkey); + if (ret) { + if (type3.lm.data) + free(type3.lm.data); + if (type3.ntlm.data) + free(type3.ntlm.data); + goto error; + } + + free(tmpsesskey.data); + flags |= NTLM_FLAG_SESSIONKEY; +#if 0 + } else { + struct ntlm_buf sessionkey; + unsigned char ntlmv2[16]; + struct ntlm_targetinfo ti; + + /* verify infotarget */ + + ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); + if(ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } + + if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, + ctx->client->key.length, + type3.username, + name->domain, + type2.challenge, + &type2.targetinfo, + ntlmv2, + &type3.ntlm); + if (ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), + &sessionkey, + &type3.sessionkey); + memset(ntlmv2, 0, sizeof(ntlmv2)); + if (ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } + + flags |= NTLM_FLAG_NTLM2_SESSION | + NTLM_FLAG_SESSION; + + if (type3.flags & NTLM_NEG_KEYEX) + flags |= NTLM_FLAG_KEYEX; + + ret = krb5_data_copy(&ctx->sessionkey, + sessionkey.data, sessionkey.length); + free(sessionkey.data); + if (ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } +#endif + } + +#if 0 + if (flags & NTLM_FLAG_NTLM2_SESSION) { + _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), + ctx->sessionkey.data, + ctx->sessionkey.length); + _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), + ctx->sessionkey.data, + ctx->sessionkey.length); + } else { + flags |= NTLM_FLAG_SESSION; + RC4_set_key(&ctx->u.v1.crypto_recv.key, + ctx->sessionkey.length, + ctx->sessionkey.data); + RC4_set_key(&ctx->u.v1.crypto_send.key, + ctx->sessionkey.length, + ctx->sessionkey.data); + } +#endif + + ret = heim_ntlm_encode_type3(&type3, &ndata, NULL); + if (ret) + goto error; + + data.data = ndata.data; + data.length = ndata.length; + ret = krb5_store_data(response, data); + heim_ntlm_free_buf(&ndata); + if (ret) goto error; + + ret = krb5_store_int32(response, flags); + if (ret) goto error; + + data.data = sessionkey.data; + data.length = sessionkey.length; + + ret = krb5_store_data(response, data); + if (ret) goto error; + + error: + free(type3.username); + heim_ntlm_free_type2(&type2); + free(user); + if (domain) + free(domain); + + return ret; +} + + +/* + * { "GET_NTLM_UUID_LIST", NULL } + * + * reply: + * 1 user domain + * 0 [ end of list ] + */ + +static krb5_error_code +kcm_op_get_ntlm_user_list(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *c; + krb5_error_code ret; + + for (c = ntlm_head; c != NULL; c = c->next) { + if (!kcm_is_same_session(client, c->uid, c->session)) + continue; + + ret = krb5_store_uint32(response, 1); + if (ret) + return ret; + ret = krb5_store_stringz(response, c->user); + if (ret) + return ret; + ret = krb5_store_stringz(response, c->domain); + if (ret) + return ret; + } + return krb5_store_uint32(response, 0); +} + +/* + * + */ + +static struct kcm_op kcm_ops[] = { + { "NOOP", kcm_op_noop }, + { "GET_NAME", kcm_op_get_name }, + { "RESOLVE", kcm_op_noop }, + { "GEN_NEW", kcm_op_gen_new }, + { "INITIALIZE", kcm_op_initialize }, + { "DESTROY", kcm_op_destroy }, + { "STORE", kcm_op_store }, + { "RETRIEVE", kcm_op_retrieve }, + { "GET_PRINCIPAL", kcm_op_get_principal }, + { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list }, + { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid }, + { "REMOVE_CRED", kcm_op_remove_cred }, + { "SET_FLAGS", kcm_op_set_flags }, + { "CHOWN", kcm_op_chown }, + { "CHMOD", kcm_op_chmod }, + { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket }, + { "GET_TICKET", kcm_op_get_ticket }, + { "MOVE_CACHE", kcm_op_move_cache }, + { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list }, + { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid }, + { "GET_DEFAULT_CACHE", kcm_op_get_default_cache }, + { "SET_DEFAULT_CACHE", kcm_op_set_default_cache }, + { "GET_KDC_OFFSET", kcm_op_get_kdc_offset }, + { "SET_KDC_OFFSET", kcm_op_set_kdc_offset }, + { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred }, + { "HAVE_USER_CRED", kcm_op_have_ntlm_cred }, + { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred }, + { "DO_NTLM_AUTH", kcm_op_do_ntlm }, + { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list } +}; + + +const char * +kcm_op2string(kcm_operation opcode) +{ + if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) + return "Unknown operation"; + + return kcm_ops[opcode].name; +} + +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; + uint16_t opcode; + + krb5_data_zero(resp_data); + resp_sp = krb5_storage_emem(); + if (resp_sp == NULL) { + return ENOMEM; + } + + if (client->pid == -1) { + 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; + } + + ret = krb5_ret_uint16(req_sp, &opcode); + if (ret) { + kcm_log(0, "Process %d: didn't send a message", client->pid); + goto out; + } + + if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) { + kcm_log(0, "Process %d: invalid operation code %d", + client->pid, opcode); + ret = KRB5_FCC_INTERNAL; + goto out; + } + method = kcm_ops[opcode].method; + if (method == NULL) { + kcm_log(0, "Process %d: operation code %s not implemented", + client->pid, kcm_op2string(opcode)); + ret = KRB5_FCC_INTERNAL; + goto out; + } + + /* 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); + } + + if (resp_sp) { + krb5_error_code ret2; + + krb5_storage_seek(resp_sp, 0, SEEK_SET); + ret2 = krb5_store_int32(resp_sp, ret); + if (ret2 == 0) + ret2 = krb5_storage_to_data(resp_sp, resp_data); + krb5_storage_free(resp_sp); + if (ret2) + ret = ret2; + } + + return ret; +} + diff --git a/third_party/heimdal/kcm/renew.c b/third_party/heimdal/kcm/renew.c new file mode 100644 index 0000000..7b31965 --- /dev/null +++ b/third_party/heimdal/kcm/renew.c @@ -0,0 +1,133 @@ +/* + * 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; + const char *estr; + + 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) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to copy service principal: %s", + estr); + krb5_free_error_message(context, estr); + 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) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to make TGS principal for realm %s: %s", + realm, estr); + krb5_free_error_message(context, estr); + 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) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to renew credentials for cache %s: %s", + ccache->name, estr); + krb5_free_error_message(context, estr); + 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) { + estr = krb5_get_error_message(context, ret); + kcm_log(0, "Failed to store credentials for cache %s: %s", + ccache->name, estr); + krb5_free_error_message(context, estr); + krb5_free_creds(context, out); + goto out; + } + + free(out); /* but not contents */ + +out: + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + return ret; +} + diff --git a/third_party/heimdal/kcm/sessions.c b/third_party/heimdal/kcm/sessions.c new file mode 100644 index 0000000..4e66cc5 --- /dev/null +++ b/third_party/heimdal/kcm/sessions.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2009 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 Apple Inc. 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" + +#if 0 +#include +#endif + +void +kcm_session_add(pid_t session_id) +{ + kcm_log(1, "monitor session: %d\n", session_id); +} + +void +kcm_session_setup_handler(void) +{ +#if 0 + au_sdev_handle_t *h; + dispatch_queue_t bgq; + + h = au_sdev_open(AU_SDEVF_ALLSESSIONS); + if (h == NULL) + return; + + bgq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); + + dispatch_async(bgq, ^{ + for (;;) { + auditinfo_addr_t aio; + int event; + + if (au_sdev_read_aia(h, &event, &aio) != 0) + continue; + + /* + * Ignore everything but END. This should relly be + * CLOSE but since that is delayed until the credential + * is reused, we can't do that + * */ + if (event != AUE_SESSION_END) + continue; + + dispatch_async(dispatch_get_main_queue(), ^{ + kcm_cache_remove_session(aio.ai_asid); + }); + } + }); +#endif +} -- cgit v1.2.3