summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/kcm
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/kcm')
-rw-r--r--third_party/heimdal/kcm/Makefile.am45
-rw-r--r--third_party/heimdal/kcm/NTMakefile35
-rw-r--r--third_party/heimdal/kcm/acl.c201
-rw-r--r--third_party/heimdal/kcm/acquire.c149
-rw-r--r--third_party/heimdal/kcm/cache.c678
-rw-r--r--third_party/heimdal/kcm/client.c223
-rw-r--r--third_party/heimdal/kcm/config.c423
-rw-r--r--third_party/heimdal/kcm/connect.c84
-rw-r--r--third_party/heimdal/kcm/events.c444
-rw-r--r--third_party/heimdal/kcm/glue.c303
-rw-r--r--third_party/heimdal/kcm/headers.h90
-rw-r--r--third_party/heimdal/kcm/kcm.8168
-rw-r--r--third_party/heimdal/kcm/kcm_locl.h186
-rw-r--r--third_party/heimdal/kcm/log.c85
-rw-r--r--third_party/heimdal/kcm/main.c123
-rw-r--r--third_party/heimdal/kcm/protocol.c1812
-rw-r--r--third_party/heimdal/kcm/renew.c133
-rw-r--r--third_party/heimdal/kcm/sessions.c83
18 files changed, 5265 insertions, 0 deletions
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) ? "<none>" : cpn,
+ (spn == NULL) ? "<none>" : 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 <pwd.h>
+
+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 <getarg.h>
+#include <parse_bytes.h>
+
+#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 : "<none>");
+
+ 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 <config.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#include <ctype.h>
+#include <err.h>
+#include <roken.h>
+#include <getarg.h>
+#include <base64.h>
+#include <parse_units.h>
+#include <parse_time.h>
+
+
+#include <krb5.h>
+#include <heim_threads.h>
+
+#include <heim-ipc.h>
+
+#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 <kcm.h>
+
+#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 <kcm-protos.h>
+
+#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 <heimntlm.h>
+
+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, &not_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 <bsm/audit_session.h>
+#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
+}