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