darling-MITKerberosShim/ccache.c
2020-07-20 19:29:57 -04:00

1314 lines
34 KiB
C

/*
* Copyright (c) 2004 - 2007 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 "mit-CredentialsCache.h"
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include "heim.h"
static cc_time_t context_change_time = 0;
void
update_time(cc_time_t *change_time)
{
cc_time_t now = (cc_time_t)time(NULL);
if (*change_time >= now)
*change_time += 1;
else
*change_time = now;
}
static cc_int32
string_release(cc_string_t io_string)
{
free((char *)io_string->data);
free(io_string);
return ccNoError;
}
cc_string_f string_functions = {
.release = string_release
};
static cc_string_t
create_string(const char *string)
{
cc_string_t s;
s = mshim_malloc(sizeof(*s));
s->functions = &string_functions;
s->data = strdup(string);
return s;
}
#define KRB5_CCAPI_TKT_FLG_FORWARDABLE 0x40000000
#define KRB5_CCAPI_TKT_FLG_FORWARDED 0x20000000
#define KRB5_CCAPI_TKT_FLG_PROXIABLE 0x10000000
#define KRB5_CCAPI_TKT_FLG_PROXY 0x08000000
#define KRB5_CCAPI_TKT_FLG_MAY_POSTDATE 0x04000000
#define KRB5_CCAPI_TKT_FLG_POSTDATED 0x02000000
#define KRB5_CCAPI_TKT_FLG_INVALID 0x01000000
#define KRB5_CCAPI_TKT_FLG_RENEWABLE 0x00800000
#define KRB5_CCAPI_TKT_FLG_INITIAL 0x00400000
#define KRB5_CCAPI_TKT_FLG_PRE_AUTH 0x00200000
#define KRB5_CCAPI_TKT_FLG_HW_AUTH 0x00100000
#define KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000
#define KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE 0x00040000
#define KRB5_CCAPI_TKT_FLG_ANONYMOUS 0x00020000
static krb5_error_code
make_cred_from_ccred(krb5_context context,
const cc_credentials_v5_t *incred,
krb5_creds *cred)
{
krb5_error_code ret;
unsigned int i;
memset(cred, 0, sizeof(*cred));
ret = heim_krb5_parse_name(context, incred->client, &cred->client);
if (ret)
goto fail;
ret = heim_krb5_parse_name(context, incred->server, &cred->server);
if (ret)
goto fail;
cred->session.keytype = incred->keyblock.type;
cred->session.keyvalue.length = incred->keyblock.length;
cred->session.keyvalue.data = malloc(incred->keyblock.length);
if (cred->session.keyvalue.data == NULL)
goto nomem;
memcpy(cred->session.keyvalue.data, incred->keyblock.data,
incred->keyblock.length);
cred->times.authtime = incred->authtime;
cred->times.starttime = incred->starttime;
cred->times.endtime = incred->endtime;
cred->times.renew_till = incred->renew_till;
ret = heim_krb5_data_copy(&cred->ticket,
incred->ticket.data,
incred->ticket.length);
if (ret)
goto nomem;
ret = heim_krb5_data_copy(&cred->second_ticket,
incred->second_ticket.data,
incred->second_ticket.length);
if (ret)
goto nomem;
cred->authdata.val = NULL;
cred->authdata.len = 0;
cred->addresses.val = NULL;
cred->addresses.len = 0;
for (i = 0; incred->authdata && incred->authdata[i]; i++)
;
if (i) {
cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
if (cred->authdata.val == NULL)
goto nomem;
cred->authdata.len = i;
for (i = 0; i < cred->authdata.len; i++) {
cred->authdata.val[i].ad_type = incred->authdata[i]->type;
ret = heim_krb5_data_copy(&cred->authdata.val[i].ad_data,
incred->authdata[i]->data,
incred->authdata[i]->length);
if (ret)
goto nomem;
}
}
for (i = 0; incred->addresses && incred->addresses[i]; i++)
;
if (i) {
cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
if (cred->addresses.val == NULL)
goto nomem;
cred->addresses.len = i;
for (i = 0; i < cred->addresses.len; i++) {
cred->addresses.val[i].addr_type = incred->addresses[i]->type;
ret = heim_krb5_data_copy(&cred->addresses.val[i].address,
incred->addresses[i]->data,
incred->addresses[i]->length);
if (ret)
goto nomem;
}
}
cred->flags.i = 0;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
cred->flags.b.forwardable = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
cred->flags.b.forwarded = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
cred->flags.b.proxiable = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
cred->flags.b.proxy = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
cred->flags.b.may_postdate = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
cred->flags.b.postdated = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
cred->flags.b.invalid = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
cred->flags.b.renewable = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
cred->flags.b.initial = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
cred->flags.b.pre_authent = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
cred->flags.b.hw_authent = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
cred->flags.b.transited_policy_checked = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
cred->flags.b.ok_as_delegate = 1;
if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
cred->flags.b.anonymous = 1;
return 0;
nomem:
ret = ENOMEM;
krb5_set_error_message((mit_krb5_context)context, ret, "malloc: out of memory");
fail:
heim_krb5_free_cred_contents(context, cred);
return ret;
}
static void
free_ccred(cc_credentials_v5_t *cred)
{
int i;
if (cred->addresses) {
for (i = 0; cred->addresses[i] != 0; i++) {
if (cred->addresses[i]->data)
free(cred->addresses[i]->data);
free(cred->addresses[i]);
}
free(cred->addresses);
}
if (cred->server)
free(cred->server);
if (cred->client)
free(cred->client);
memset(cred, 0, sizeof(*cred));
}
static krb5_error_code
make_ccred_from_cred(krb5_context context,
const krb5_creds *incred,
cc_credentials_v5_t *cred)
{
krb5_error_code ret;
int i;
memset(cred, 0, sizeof(*cred));
ret = heim_krb5_unparse_name(context, incred->client, &cred->client);
if (ret)
goto fail;
ret = heim_krb5_unparse_name(context, incred->server, &cred->server);
if (ret)
goto fail;
cred->keyblock.type = incred->session.keytype;
cred->keyblock.length = (cc_uint32)incred->session.keyvalue.length;
cred->keyblock.data = incred->session.keyvalue.data;
cred->authtime = (cc_time_t)incred->times.authtime;
cred->starttime = (cc_time_t)incred->times.starttime;
cred->endtime = (cc_time_t)incred->times.endtime;
cred->renew_till = (cc_time_t)incred->times.renew_till;
cred->ticket.length = (cc_int32)incred->ticket.length;
cred->ticket.data = incred->ticket.data;
cred->second_ticket.length = (cc_int32)incred->second_ticket.length;
cred->second_ticket.data = incred->second_ticket.data;
/* XXX this one should also be filled in */
cred->authdata = NULL;
cred->addresses = calloc(incred->addresses.len + 1,
sizeof(cred->addresses[0]));
if (cred->addresses == NULL) {
ret = ENOMEM;
goto fail;
}
for (i = 0; i < incred->addresses.len; i++) {
cc_data *addr;
addr = malloc(sizeof(*addr));
if (addr == NULL) {
ret = ENOMEM;
goto fail;
}
addr->type = incred->addresses.val[i].addr_type;
addr->length = (cc_int32)incred->addresses.val[i].address.length;
addr->data = malloc(addr->length);
if (addr->data == NULL) {
free(addr);
ret = ENOMEM;
goto fail;
}
memcpy(addr->data, incred->addresses.val[i].address.data,
addr->length);
cred->addresses[i] = addr;
}
cred->addresses[i] = NULL;
cred->ticket_flags = 0;
if (incred->flags.b.forwardable)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
if (incred->flags.b.forwarded)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
if (incred->flags.b.proxiable)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
if (incred->flags.b.proxy)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
if (incred->flags.b.may_postdate)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
if (incred->flags.b.postdated)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
if (incred->flags.b.invalid)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
if (incred->flags.b.renewable)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
if (incred->flags.b.initial)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
if (incred->flags.b.pre_authent)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
if (incred->flags.b.hw_authent)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
if (incred->flags.b.transited_policy_checked)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
if (incred->flags.b.ok_as_delegate)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
if (incred->flags.b.anonymous)
cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
return 0;
fail:
free_ccred(cred);
krb5_clear_error_message((mit_krb5_context)context);
return ret;
}
/*
*
*/
struct cred {
cc_credentials_union *data;
cc_credentials_f *functions;
cc_credentials_f *otherFunctions;
krb5_creds cred;
};
static cc_int32
cred_release(cc_credentials_t io_credentials)
{
struct cred *c = (struct cred *)io_credentials;
heim_krb5_free_cred_contents(milcontext, &c->cred);
free(c->data->credentials.credentials_v5);
free(c->data);
free(c);
return ccNoError;
}
static cc_int32
cred_compare (cc_credentials_t in_credentials,
cc_credentials_t in_compare_to_credentials,
cc_uint32 *out_equal)
{
*out_equal = 1;
return ccErrNoMem;
}
cc_credentials_f credential_functions = {
.release = cred_release,
.compare = cred_compare
};
static cc_credentials_t
create_credentials(krb5_creds *cred)
{
struct cred *c;
c = calloc(1, sizeof(*c));
c->data = calloc(1, sizeof(*c->data));
c->data->version = cc_credentials_v5;
c->data->credentials.credentials_v5 = calloc(1, sizeof(*c->data->credentials.credentials_v5));
c->functions = &credential_functions;
heim_krb5_copy_creds_contents(milcontext, cred, &c->cred);
make_ccred_from_cred(milcontext, &c->cred, c->data->credentials.credentials_v5);
return (cc_credentials_t)c;
}
struct cred_iterator {
cc_credentials_iterator_d iterator;
krb5_ccache id;
krb5_cc_cursor cursor;
};
static cc_int32
cred_iter_release(cc_credentials_iterator_t io_credentials_iterator)
{
struct cred_iterator *ci = (struct cred_iterator *)io_credentials_iterator;
LOG_ENTRY();
if (ci->id)
krb5_cc_end_seq_get ((mit_krb5_context)milcontext,
(mit_krb5_ccache)ci->id,
(mit_krb5_cc_cursor *)&ci->cursor);
free(ci);
return ccNoError;
}
cc_int32
cred_iter_next(cc_credentials_iterator_t in_credentials_iterator, cc_credentials_t *out_credentials)
{
struct cred_iterator *ci = (struct cred_iterator *)in_credentials_iterator;
krb5_error_code ret;
krb5_creds cred;
LOG_ENTRY();
ret = heim_krb5_cc_next_cred(milcontext, ci->id, &ci->cursor, &cred);
if (ret == KRB5_CC_END)
return ccIteratorEnd;
else if (ret)
return ret; /* XXX */
*out_credentials = create_credentials(&cred);
heim_krb5_free_cred_contents(milcontext, &cred);
if (*out_credentials == NULL)
return ccErrNoMem;
return ccNoError;
}
cc_int32
cred_iter_clone (cc_credentials_iterator_t in_credentials_iterator,
cc_credentials_iterator_t *out_credentials_iterator)
{
LOG_UNIMPLEMENTED();
return ccErrNoMem;
}
struct cc_credentials_iterator_f cred_iter_functions = {
.release = cred_iter_release,
.next = cred_iter_next,
.clone = cred_iter_clone
};
struct cc_ccache {
cc_ccache_d ccache;
krb5_ccache id;
cc_time_t change_time;
cc_time_t last_default_time;
};
cc_int32
ccache_release(cc_ccache_t io_ccache)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
LOG_ENTRY();
if (c->id)
heim_krb5_cc_close(milcontext, c->id);
free(c);
return ccNoError;
}
static cc_int32
ccache_destroy(cc_ccache_t io_ccache)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
LOG_ENTRY();
update_time(&context_change_time);
if (c->id) {
krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)c->id);
c->id = NULL;
}
return ccNoError;
}
static cc_int32
ccache_set_default(cc_ccache_t io_ccache)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
LOG_ENTRY();
if (io_ccache == NULL || c->id == NULL)
return ccErrBadParam;
heim_krb5_cc_switch(milcontext, c->id);
update_time(&c->change_time);
update_time(&c->last_default_time);
return ccNoError;
}
static cc_int32
ccache_get_credentials_version(cc_ccache_t in_ccache, cc_uint32 *out_credentials_version)
{
if (out_credentials_version == NULL)
return ccErrBadParam;
*out_credentials_version = cc_credentials_v5;
return ccNoError;
}
static cc_int32
ccache_get_name(cc_ccache_t in_ccache, cc_string_t *out_name)
{
struct cc_ccache *c = (struct cc_ccache *)in_ccache;
const char *name;
LOG_ENTRY();
if (out_name == NULL)
return ccErrBadParam;
if (c->id == NULL)
return ccErrInvalidCCache;
name = heim_krb5_cc_get_name(milcontext, c->id);
if (name == NULL)
return ccErrInvalidCCache;
*out_name = create_string(name);
return ccNoError;
}
static cc_int32
ccache_get_principal(cc_ccache_t in_ccache, cc_uint32 in_credentials_version, cc_string_t *out_principal)
{
struct cc_ccache *c = (struct cc_ccache *)in_ccache;
krb5_principal princ;
krb5_error_code ret;
char *name;
LOG_ENTRY();
if (out_principal == NULL)
return ccErrBadParam;
if (in_credentials_version != cc_credentials_v5)
return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version");
if (c->id == NULL)
return ccErrInvalidCCache;
ret = heim_krb5_cc_get_principal(milcontext, c->id, &princ);
if (ret)
return LOG_FAILURE(ret, "get principal");
ret = heim_krb5_unparse_name(milcontext, princ, &name);
heim_krb5_free_principal(milcontext, princ);
if (ret)
return LOG_FAILURE(ret, "unparse name");
*out_principal = create_string(name);
free(name);
return ccNoError;
}
static cc_int32
ccache_set_principal(cc_ccache_t io_ccache, cc_uint32 in_credentials_version, const char *in_principal)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
krb5_error_code ret;
krb5_principal p;
LOG_ENTRY();
if (in_principal == NULL)
return ccErrBadParam;
if (in_credentials_version != cc_credentials_v5)
return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version");
update_time(&c->change_time);
update_time(&context_change_time);
ret = heim_krb5_parse_name(milcontext, in_principal, &p);
if (ret)
return LOG_FAILURE(ccErrBadParam, "parse name");
ret = heim_krb5_cc_initialize(milcontext, c->id, p);
heim_krb5_free_principal(milcontext, p);
if (ret)
return LOG_FAILURE(ccErrInvalidCCache, "init cache");
return ccNoError;
}
static cc_int32
ccache_store_credentials(cc_ccache_t io_ccache, const cc_credentials_union *in_credentials_union)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
krb5_error_code ret;
krb5_creds hcred;
LOG_ENTRY();
if (in_credentials_union == NULL)
return ccErrBadParam;
if (in_credentials_union->version != cc_credentials_v5)
return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version");
if (in_credentials_union->credentials.credentials_v5->client == NULL)
return ccErrBadParam;
update_time(&c->change_time);
update_time(&context_change_time);
make_cred_from_ccred(milcontext, in_credentials_union->credentials.credentials_v5, &hcred);
ret = heim_krb5_cc_store_cred(milcontext, c->id, &hcred);
heim_krb5_free_cred_contents(milcontext, &hcred);
if (ret)
return LOG_FAILURE(ccErrInvalidCCache, "store cred");
return ccNoError;
}
static cc_int32
ccache_remove_credentials(cc_ccache_t io_ccache, cc_credentials_t in_credentials)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
const cc_credentials_v5_t *incred;
krb5_creds cred;
krb5_error_code ret;
LOG_ENTRY();
update_time(&c->change_time);
update_time(&context_change_time);
memset(&cred, 0, sizeof(cred));
if (c->id == NULL)
return LOG_FAILURE(ccErrBadParam, "bad argument");
if (in_credentials == NULL || in_credentials->data == NULL)
return LOG_FAILURE(ccErrBadParam, "remove with no cred?");
if (in_credentials->data->version != cc_credentials_v5)
return LOG_FAILURE(ccErrBadParam, "wrong version");
incred = in_credentials->data->credentials.credentials_v5;
if (incred->client == NULL)
return LOG_FAILURE(ccErrBadParam, "no client to remove");
if (incred->server == NULL)
return LOG_FAILURE(ccErrBadParam, "no server to remove");
ret = heim_krb5_parse_name(milcontext, incred->client, &cred.client);
if (ret)
goto fail;
ret = heim_krb5_parse_name(milcontext, incred->server, &cred.server);
if (ret)
goto fail;
ret = heim_krb5_cc_remove_cred(milcontext, c->id, 0, &cred);
update_time(&context_change_time);
fail:
heim_krb5_free_cred_contents(milcontext, &cred);
if (ret)
return ccErrCredentialsNotFound;
return ccNoError;
}
static cc_int32
ccache_new_credentials_iterator(cc_ccache_t in_ccache, cc_credentials_iterator_t *out_credentials_iterator)
{
struct cc_ccache *c = (struct cc_ccache *)in_ccache;
struct cred_iterator *ci;
krb5_error_code ret;
LOG_ENTRY();
if (c == NULL || c->id == NULL)
return ccErrInvalidCCache;
if (out_credentials_iterator == NULL)
return ccErrBadParam;
ci = calloc(1, sizeof(*ci));
ci->iterator.functions = &cred_iter_functions;
ci->id = c->id;
ret = krb5_cc_start_seq_get((mit_krb5_context)milcontext,
(mit_krb5_ccache)c->id,
(mit_krb5_cc_cursor *)&ci->cursor);
if (ret) {
free(ci);
return LOG_FAILURE(ccErrInvalidCCache, "start seq");
}
*out_credentials_iterator = (cc_credentials_iterator_t)ci;
return ccNoError;
}
static cc_int32
ccache_move(cc_ccache_t io_source_ccache, cc_ccache_t io_destination_ccache)
{
struct cc_ccache *s = (struct cc_ccache *)io_source_ccache;
struct cc_ccache *d = (struct cc_ccache *)io_destination_ccache;
krb5_error_code ret;
if (s->id == NULL)
return ccErrInvalidCCache;
if (d == NULL)
return ccErrBadParam;
if (d->id == NULL) {
ret = heim_krb5_cc_new_unique(milcontext,
heim_krb5_cc_get_type(milcontext, s->id),
NULL, &d->id);
if (ret)
return ccErrInvalidCCache;
}
ret = heim_krb5_cc_move(milcontext, s->id, d->id);
if (ret)
return LOG_FAILURE(ret, "move cache");
s->id = NULL;
return ccNoError;
}
static cc_int32
ccache_lock(cc_ccache_t io_ccache, cc_uint32 in_lock_type, cc_uint32 in_block)
{
LOG_ENTRY();
return ccNoError;
}
static cc_int32
ccache_unlock(cc_ccache_t io_ccache)
{
LOG_ENTRY();
return ccNoError;
}
static cc_int32
ccache_get_last_default_time(cc_ccache_t in_ccache, cc_time_t *out_last_default_time)
{
struct cc_ccache *s = (struct cc_ccache *)in_ccache;
LOG_ENTRY();
if (out_last_default_time == NULL)
return ccErrBadParam;
if (s->id == NULL)
return ccErrInvalidCCache;
if (s->last_default_time == 0)
return ccErrNeverDefault;
*out_last_default_time = s->last_default_time;
return ccNoError;
}
static cc_int32
ccache_get_change_time(cc_ccache_t in_ccache, cc_time_t *out_change_time)
{
struct cc_ccache *s = (struct cc_ccache *)in_ccache;
LOG_ENTRY();
if (out_change_time == NULL)
return ccErrBadParam;
*out_change_time = s->change_time;
return ccNoError;
}
static cc_int32
ccache_compare(cc_ccache_t in_ccache, cc_ccache_t in_compare_to_ccache, cc_uint32 *out_equal)
{
struct cc_ccache *s1 = (struct cc_ccache *)in_ccache;
struct cc_ccache *s2 = (struct cc_ccache *)in_compare_to_ccache;
krb5_error_code ret;
char *n1, *n2;
LOG_ENTRY();
if (out_equal == NULL || s2 == NULL)
return ccErrBadParam;
if (s1 == s2) {
*out_equal = 1;
return ccNoError;
}
if (s1->id == NULL || s2->id == NULL)
return ccErrInvalidCCache;
ret = heim_krb5_cc_get_full_name(milcontext, s1->id, &n1);
if (ret)
return ccErrInvalidCCache;
ret = heim_krb5_cc_get_full_name(milcontext, s2->id, &n2);
if (ret) {
free(n1);
return ccErrInvalidCCache;
}
*out_equal = (strcmp(n1, n2) == 0);
free(n1);
free(n2);
return ccNoError;
}
static cc_int32
ccache_get_kdc_time_offset(cc_ccache_t in_ccache,
cc_uint32 in_credentials_version,
cc_time_t *out_time_offset)
{
struct cc_ccache *c = (struct cc_ccache *)in_ccache;
krb5_deltat sec = 0;
LOG_ENTRY();
if (c->id == NULL)
return LOG_FAILURE(ccErrBadParam, "bad credential");
if (in_credentials_version != cc_credentials_v5)
return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version");
if (out_time_offset == NULL)
return LOG_FAILURE(ccErrBadParam, "bad argument");
heim_krb5_cc_get_kdc_offset(milcontext, c->id, &sec);
*out_time_offset = (cc_time_t)sec;
return ccNoError;
}
static cc_int32
ccache_set_kdc_time_offset(cc_ccache_t io_ccache, cc_uint32 in_credentials_version, cc_time_t in_time_offset)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
LOG_ENTRY();
if (c->id == NULL)
return LOG_FAILURE(ccErrBadParam, "bad credential");
if (in_credentials_version != cc_credentials_v5)
return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version");
heim_krb5_cc_set_kdc_offset(milcontext, c->id, in_time_offset);
return ccNoError;
}
static cc_int32
ccache_clear_kdc_time_offset(cc_ccache_t io_ccache, cc_uint32 in_credentials_version)
{
struct cc_ccache *c = (struct cc_ccache *)io_ccache;
LOG_ENTRY();
if (c->id == NULL)
return LOG_FAILURE(ccErrBadParam, "bad credential");
heim_krb5_cc_set_kdc_offset(milcontext, c->id, 0);
return ccNoError;
}
static cc_int32
ccache_wait_for_change(cc_ccache_t in_ccache)
{
LOG_UNIMPLEMENTED();
return ccErrNoMem;
}
static cc_ccache_f ccache_functions = {
.release = ccache_release,
.destroy = ccache_destroy,
.set_default = ccache_set_default,
.get_credentials_version = ccache_get_credentials_version,
.get_name = ccache_get_name,
.get_principal = ccache_get_principal,
.set_principal = ccache_set_principal,
.store_credentials = ccache_store_credentials,
.remove_credentials = ccache_remove_credentials,
.new_credentials_iterator = ccache_new_credentials_iterator,
.move = ccache_move,
.lock = ccache_lock,
.unlock = ccache_unlock,
.get_last_default_time = ccache_get_last_default_time,
.get_change_time = ccache_get_change_time,
.compare = ccache_compare,
.get_kdc_time_offset = ccache_get_kdc_time_offset,
.set_kdc_time_offset = ccache_set_kdc_time_offset,
.clear_kdc_time_offset = ccache_clear_kdc_time_offset,
.wait_for_change = ccache_wait_for_change
};
static cc_ccache_t
create_ccache(krb5_ccache id)
{
struct cc_ccache *c;
c = mshim_malloc(sizeof(*c));
c->ccache.functions = &ccache_functions;
c->id = id;
update_time(&c->change_time);
return (cc_ccache_t)c;
}
struct cc_iter {
cc_ccache_iterator_d iterator;
mit_krb5_cccol_cursor cursor;
};
static cc_int32
cc_iterator_release(cc_ccache_iterator_t io_ccache_iterator)
{
struct cc_iter *c = (struct cc_iter *)io_ccache_iterator;
LOG_ENTRY();
krb5_cccol_cursor_free((mit_krb5_context)milcontext, &c->cursor);
free(c);
return ccNoError;
}
cc_int32
cc_iterator_next(cc_ccache_iterator_t in_ccache_iterator,
cc_ccache_t *out_ccache)
{
struct cc_iter *c = (struct cc_iter *)in_ccache_iterator;
krb5_error_code ret;
krb5_ccache id;
LOG_ENTRY();
if (out_ccache == NULL)
return ccErrBadParam;
while (1) {
ret = krb5_cccol_cursor_next((mit_krb5_context)milcontext, c->cursor, (mit_krb5_ccache *)&id);
if (ret == KRB5_CC_END || id == NULL)
return ccIteratorEnd;
else if (ret)
return LOG_FAILURE(ret, "ccol next cursor");
const char *type = heim_krb5_cc_get_type(milcontext, id);
if (strcmp(type, "API") == 0 || strcmp(type, "KCM") == 0)
break;
heim_krb5_cc_close(milcontext, id);
}
*out_ccache = create_ccache(id);
return ccNoError;
}
static cc_int32
cc_iterator_clone(cc_ccache_iterator_t in_ccache_iterator,
cc_ccache_iterator_t *out_ccache_iterator)
{
LOG_UNIMPLEMENTED();
if (out_ccache_iterator == NULL)
return ccErrBadParam;
return ccErrNoMem;
}
static cc_ccache_iterator_f ccache_iterator_functions = {
.release = cc_iterator_release,
.next = cc_iterator_next,
.clone = cc_iterator_clone
};
static cc_int32
context_release(cc_context_t io_context)
{
LOG_ENTRY();
memset(io_context, 0, sizeof(*io_context));
free(io_context);
return ccNoError;
}
static cc_int32
context_get_change_time(cc_context_t in_context,
cc_time_t *out_time)
{
LOG_ENTRY();
if (out_time == NULL)
return ccErrBadParam;
*out_time = context_change_time;
return ccNoError;
}
static cc_int32
context_get_default_ccache_name(cc_context_t in_context,
cc_string_t *out_name)
{
const char *name;
name = krb5_cc_default_name((mit_krb5_context)milcontext);
if (name == NULL)
return ccErrNoMem; /* XXX */
if (out_name == NULL)
return ccErrBadParam;
if (strncmp("API:", name, 4) == 0)
name += 4;
*out_name = create_string(name);
return ccNoError;
}
/*
* Probe for client principal to make sure the cache really
* exists.
*/
static cc_int32
check_exists(krb5_ccache id)
{
krb5_principal princ;
int ret;
ret = heim_krb5_cc_get_principal(milcontext, id, &princ);
if (ret)
return 0;
heim_krb5_free_principal(milcontext, princ);
return 1;
}
static cc_int32
context_open_ccache (cc_context_t in_context,
const char *in_name,
cc_ccache_t *out_ccache)
{
char *name;
krb5_error_code ret;
krb5_ccache id;
if (out_ccache == NULL || in_name == NULL || in_context == NULL)
return ccErrBadParam;
asprintf(&name, "API:%s", in_name);
ret = heim_krb5_cc_resolve(milcontext, name, &id);
free(name);
if (ret)
return LOG_FAILURE(ret, "open cache");
if (!check_exists(id)) {
heim_krb5_cc_close(milcontext, id);
return ccErrCCacheNotFound;
}
*out_ccache = create_ccache(id);
return ccNoError;
}
static cc_int32
context_open_default_ccache(cc_context_t in_context,
cc_ccache_t *out_ccache)
{
krb5_error_code ret;
krb5_ccache id;
LOG_ENTRY();
if (out_ccache == NULL)
return ccErrBadParam;
ret = heim_krb5_cc_default(milcontext, &id);
if (ret)
return LOG_FAILURE(ret, "cc default");
if (!check_exists(id)) {
heim_krb5_cc_close(milcontext, id);
return ccErrCCacheNotFound;
}
*out_ccache = create_ccache(id);
return ccNoError;
}
static cc_int32
context_create_ccache(cc_context_t in_context,
const char *in_name,
cc_uint32 in_cred_vers,
const char *in_principal,
cc_ccache_t *out_ccache)
{
krb5_principal principal;
krb5_error_code ret;
krb5_ccache id;
if (in_cred_vers != cc_credentials_v5)
return ccErrBadCredentialsVersion;
if (out_ccache == NULL || in_name == NULL || in_context == NULL || in_principal == NULL)
return ccErrBadParam;
update_time(&context_change_time);
ret = heim_krb5_parse_name(milcontext, in_principal, &principal);
if (ret)
return LOG_FAILURE(ret, "parse name");
ret = heim_krb5_cc_resolve(milcontext, in_name, &id);
if (ret) {
heim_krb5_free_principal(milcontext, principal);
return LOG_FAILURE(ret, "open cache");
}
ret = heim_krb5_cc_initialize(milcontext, id, principal);
heim_krb5_free_principal(milcontext, principal);
if (ret) {
krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)id);
return LOG_FAILURE(ret, "cc init");
}
*out_ccache = create_ccache(id);
return ccNoError;
}
static cc_int32
context_create_default_ccache(cc_context_t in_context,
cc_uint32 in_cred_vers,
const char *in_principal,
cc_ccache_t *out_ccache)
{
krb5_principal principal;
krb5_error_code ret;
struct cc_ccache *c;
krb5_ccache id;
LOG_ENTRY();
if (in_cred_vers != cc_credentials_v5)
return ccErrBadCredentialsVersion;
if (out_ccache == NULL || in_principal == NULL)
return ccErrBadParam;
*out_ccache = NULL;
update_time(&context_change_time);
ret = heim_krb5_cc_default(milcontext, &id);
if (ret)
return LOG_FAILURE(ret, "cc default");
ret = heim_krb5_parse_name(milcontext, in_principal, &principal);
if (ret) {
heim_krb5_cc_close(milcontext, id);
return LOG_FAILURE(ret, "parse name");
}
ret = heim_krb5_cc_initialize(milcontext, id, principal);
heim_krb5_free_principal(milcontext, principal);
if (ret) {
krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)id);
return LOG_FAILURE(ret, "cc init");
}
c = (struct cc_ccache *)create_ccache(id);
update_time(&c->last_default_time);
*out_ccache = (cc_ccache_t)c;
return ccNoError;
}
static cc_int32
context_create_new_ccache(cc_context_t in_context,
cc_uint32 in_cred_vers,
const char *in_principal,
cc_ccache_t *out_ccache)
{
krb5_principal principal;
krb5_error_code ret;
krb5_ccache id;
LOG_ENTRY();
if (in_cred_vers != cc_credentials_v5)
return ccErrBadCredentialsVersion;
if (out_ccache == NULL || in_principal == NULL)
return ccErrBadParam;
update_time(&context_change_time);
ret = heim_krb5_parse_name(milcontext, in_principal, &principal);
if (ret)
return LOG_FAILURE(ret, "parse name");
ret = heim_krb5_cc_new_unique(milcontext, NULL, NULL, &id);
if (ret) {
heim_krb5_free_principal(milcontext, principal);
return LOG_FAILURE(ret, "new unique");
}
ret = heim_krb5_cc_initialize(milcontext, id, principal);
heim_krb5_free_principal(milcontext, principal);
if (ret) {
krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)id);
return LOG_FAILURE(ret, "cc init");
}
*out_ccache = create_ccache(id);
return ccNoError;
}
static cc_int32
context_new_ccache_iterator(cc_context_t in_context,
cc_ccache_iterator_t *out_iterator)
{
LOG_ENTRY();
krb5_error_code ret;
struct cc_iter *c;
if (out_iterator == NULL)
return ccErrBadParam;
c = calloc(1, sizeof(*c));
c->iterator.functions = &ccache_iterator_functions;
ret = krb5_cccol_cursor_new((mit_krb5_context)milcontext, &c->cursor);
if (ret) {
free(c);
return ccErrNoMem;
}
*out_iterator = (cc_ccache_iterator_t)c;
return ccNoError;
}
static cc_int32
context_lock(cc_context_t in_context,
cc_uint32 in_lock_type,
cc_uint32 in_block)
{
LOG_UNIMPLEMENTED();
return ccNoError;
}
static cc_int32
context_unlock(cc_context_t in_context)
{
LOG_UNIMPLEMENTED();
return ccNoError;
}
static cc_int32
context_compare(cc_context_t in_cc_context,
cc_context_t in_compare_to_context,
cc_uint32 *out_equal)
{
LOG_UNIMPLEMENTED();
if (out_equal == NULL || in_compare_to_context == NULL)
return ccErrBadParam;
*out_equal = (in_cc_context == in_compare_to_context);
return 0;
}
static cc_int32
context_wait_for_change(cc_context_t in_cc_context)
{
LOG_UNIMPLEMENTED();
return ccErrNoMem;
}
cc_context_f cc_functions = {
.release = context_release,
.get_change_time = context_get_change_time,
.get_default_ccache_name = context_get_default_ccache_name,
.open_ccache = context_open_ccache,
.open_default_ccache = context_open_default_ccache,
.create_ccache = context_create_ccache,
.create_default_ccache = context_create_default_ccache,
.create_new_ccache = context_create_new_ccache,
.new_ccache_iterator = context_new_ccache_iterator,
.lock = context_lock,
.unlock = context_unlock,
.compare = context_compare,
.wait_for_change = context_wait_for_change
};
cc_int32
cc_initialize(cc_context_t *out_context,
cc_int32 in_version,
cc_int32 *out_supported_version,
char const **out_vendor)
{
LOG_ENTRY();
update_time(&context_change_time);
if (in_version < ccapi_version_3 || in_version > ccapi_version_7)
return ccErrBadAPIVersion;
if (out_context == NULL)
return ccErrBadParam;
*out_context = calloc(1, sizeof(**out_context));
(*out_context)->functions = &cc_functions;
if (out_supported_version)
*out_supported_version = ccapi_version_7;
if (out_vendor)
*out_vendor = "Apple Heimdal shim layer";
return 0;
}