Skip to content

Commit

Permalink
Add support to store time offsets in cc_keyring
Browse files Browse the repository at this point in the history
The code follows the same model used for the memory ccache type.  Time
offsets are stored in each credential cache in a special key just like
the principal name.  Legacy session caches do not store timestamps as
legacy code would fail when iterating over the new offset key.

[ghudson@mit.edu: minor formatting changes; note legacy session
exception in commit message]

ticket: 7768 (new)
target_version: 1.12
tags: pullup
  • Loading branch information
simo5 authored and greghudson committed Nov 15, 2013
1 parent 7a7736a commit fb4817a
Showing 1 changed file with 203 additions and 2 deletions.
205 changes: 203 additions & 2 deletions src/lib/krb5/ccache/cc_keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ debug_print(char *fmt, ...)
*/
#define KRCC_PERSISTENT_KEYRING_NAME "_krb"

/*
* Name of the key holding time offsets for the individual cache
*/
#define KRCC_TIME_OFFSETS "__krb5_time_offsets__"

/*
* Keyring name prefix and length of random name part
*/
Expand Down Expand Up @@ -217,6 +222,7 @@ typedef struct _krb5_krcc_cursor
int numkeys;
int currkey;
key_serial_t princ_id;
key_serial_t offsets_id;
key_serial_t *keys;
} *krb5_krcc_cursor;

Expand Down Expand Up @@ -340,6 +346,12 @@ static krb5_error_code krb5_krcc_save_principal

static krb5_error_code krb5_krcc_retrieve_principal
(krb5_context context, krb5_ccache id, krb5_principal * princ);
static krb5_error_code krb5_krcc_save_time_offsets
(krb5_context context, krb5_ccache id, krb5_int32 time_offset,
krb5_int32 usec_offset);
static krb5_error_code krb5_krcc_get_time_offsets
(krb5_context context, krb5_ccache id, krb5_int32 *time_offset,
krb5_int32 *usec_offset);

/* Routines to parse a key from a keyring into a cred structure */
static krb5_error_code krb5_krcc_parse
Expand Down Expand Up @@ -410,6 +422,12 @@ krb5_krcc_parse_index(krb5_context context, krb5_int32 *version,
static krb5_error_code
krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
const char *primary, void **datapp, int *lenptr);
static krb5_error_code
krb5_krcc_parse_offsets(krb5_context context, krb5_int32 *time_offset,
krb5_int32 *usec_offset, void *payload, int psize);
static krb5_error_code
krb5_krcc_unparse_offsets(krb5_context context, krb5_int32 time_offset,
krb5_int32 usec_offset, void **datapp, int *lenptr);

/* Note the following is a stub function for Linux */
extern krb5_error_code krb5_change_cache(void);
Expand Down Expand Up @@ -835,6 +853,7 @@ krb5_krcc_initialize(krb5_context context, krb5_ccache id,
krb5_principal princ)
{
krb5_krcc_data *data = (krb5_krcc_data *)id->data;
krb5_os_context os_ctx = &context->os_context;
krb5_error_code kret;
const char *cache_name, *p;

Expand Down Expand Up @@ -863,6 +882,15 @@ krb5_krcc_initialize(krb5_context context, krb5_ccache id,
(void)keyctl_link(data->cache_id, KEY_SPEC_SESSION_KEYRING);

kret = krb5_krcc_save_principal(context, id, princ);

/* Save time offset if it is valid and this is not a legacy cache. Legacy
* applications would fail to parse the new key in the cache keyring. */
if (!is_legacy_cache_name(data->name) &&
(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
kret = krb5_krcc_save_time_offsets(context, id, os_ctx->time_offset,
os_ctx->usec_offset);
}

if (kret == KRB5_OK)
krb5_change_cache();

Expand Down Expand Up @@ -1039,6 +1067,7 @@ make_cache(key_serial_t collection_id, key_serial_t cache_id,
static krb5_error_code KRB5_CALLCONV
krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)
{
krb5_os_context os_ctx = &context->os_context;
krb5_error_code ret;
key_serial_t collection_id, cache_id;
char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
Expand Down Expand Up @@ -1067,6 +1096,19 @@ krb5_krcc_resolve(krb5_context context, krb5_ccache *id, const char *residual)

ret = make_cache(collection_id, cache_id, anchor_name, collection_name,
subsidiary_name, id);
if (ret)
goto cleanup;

/* Lookup time offsets if necessary. */
if ((context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) &&
!(os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
if (krb5_krcc_get_time_offsets(context, *id,
&os_ctx->time_offset,
&os_ctx->usec_offset) == 0) {
os_ctx->os_flags &= ~KRB5_OS_TOFFSET_TIME;
os_ctx->os_flags |= KRB5_OS_TOFFSET_VALID;
}
}

cleanup:
free(anchor_name);
Expand Down Expand Up @@ -1122,6 +1164,8 @@ krb5_krcc_start_seq_get(krb5_context context, krb5_ccache id,
}

krcursor->princ_id = d->princ_id;
krcursor->offsets_id = keyctl_search(d->cache_id, KRCC_KEY_TYPE_USER,
KRCC_TIME_OFFSETS, 0);
krcursor->numkeys = size / sizeof(key_serial_t);
krcursor->keys = keys;

Expand Down Expand Up @@ -1174,8 +1218,10 @@ krb5_krcc_next_cred(krb5_context context, krb5_ccache id,
if (krcursor->currkey >= krcursor->numkeys)
return KRB5_CC_END;

/* If we're pointing at the entry with the principal, skip it */
if (krcursor->keys[krcursor->currkey] == krcursor->princ_id) {
/* If we're pointing at the entry with the principal, or at the key
* with the time offsets, skip it. */
while (krcursor->keys[krcursor->currkey] == krcursor->princ_id ||
krcursor->keys[krcursor->currkey] == krcursor->offsets_id) {
krcursor->currkey++;
/* Check if we have now reached the end */
if (krcursor->currkey >= krcursor->numkeys)
Expand Down Expand Up @@ -1621,6 +1667,84 @@ krb5_krcc_retrieve_principal(krb5_context context, krb5_ccache id,
return kret;
}

static krb5_error_code
krb5_krcc_save_time_offsets(krb5_context context, krb5_ccache id,
krb5_int32 time_offset, krb5_int32 usec_offset)
{
krb5_krcc_data *d = (krb5_krcc_data *)id->data;
krb5_error_code kret;
key_serial_t newkey;
void *payload = NULL;
int psize;

k5_cc_mutex_assert_locked(context, &d->lock);

/* Prepare the payload. */
kret = krb5_krcc_unparse_offsets(context, time_offset, usec_offset,
&payload, &psize);
CHECK_N_GO(kret, errout);

/* Add new key into keyring. */
newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload, psize,
d->cache_id);
if (newkey == -1) {
kret = errno;
DEBUG_PRINT(("Error adding time offsets key: %s\n", strerror(kret)));
} else {
kret = KRB5_OK;
krb5_krcc_update_change_time(d);
}

errout:
free(payload);
return kret;
}

static krb5_error_code
krb5_krcc_get_time_offsets(krb5_context context, krb5_ccache id,
krb5_int32 *time_offset, krb5_int32 *usec_offset)
{
krb5_krcc_data *d = (krb5_krcc_data *)id->data;
krb5_error_code kret;
key_serial_t key;
krb5_int32 t, u;
void *payload = NULL;
int psize;

k5_cc_mutex_lock(context, &d->lock);

if (!d->cache_id) {
kret = KRB5_FCC_NOFILE;
goto errout;
}

key = keyctl_search(d->cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, 0);
if (key == -1) {
kret = ENOENT;
goto errout;
}

psize = keyctl_read_alloc(key, &payload);
if (psize == -1) {
DEBUG_PRINT(("Reading time offsets key %d: %s\n",
key, strerror(errno)));
kret = KRB5_CC_IO;
goto errout;
}

kret = krb5_krcc_parse_offsets(context, &t, &u, payload, psize);
if (kret)
goto errout;

*time_offset = t;
*usec_offset = u;

errout:
free(payload);
k5_cc_mutex_unlock(context, &d->lock);
return kret;
}

struct krcc_ptcursor_data {
key_serial_t collection_id;
char *anchor_name;
Expand Down Expand Up @@ -2656,6 +2780,83 @@ krb5_krcc_unparse_index(krb5_context context, krb5_int32 version,
return kret;
}

static krb5_error_code
krb5_krcc_parse_offsets(krb5_context context, krb5_int32 *time_offset,
krb5_int32 *usec_offset, void *payload, int psize)
{
krb5_error_code kret;
krb5_krcc_bc bc;

bc.bpp = payload;
bc.endp = bc.bpp + psize;

kret = krb5_krcc_parse_int32(context, time_offset, &bc);
CHECK_OUT(kret);

kret = krb5_krcc_parse_int32(context, usec_offset, &bc);
CHECK_OUT(kret);

return KRB5_OK;
}

static krb5_error_code
krb5_krcc_unparse_offsets_internal(krb5_context context,
krb5_int32 time_offset,
krb5_int32 usec_offset,
krb5_krcc_bc *bc)
{
krb5_error_code kret;

kret = krb5_krcc_unparse_int32(context, time_offset, bc);
CHECK_OUT(kret);

kret = krb5_krcc_unparse_int32(context, usec_offset, bc);
CHECK_OUT(kret);

return KRB5_OK;
}

static krb5_error_code
krb5_krcc_unparse_offsets(krb5_context context, krb5_int32 time_offset,
krb5_int32 usec_offset, void **datapp, int *lenptr)
{
krb5_error_code kret;
krb5_krcc_bc bc;
char *buf;

if (!datapp || !lenptr)
return EINVAL;

*datapp = NULL;
*lenptr = 0;

/* Do a dry run first to calculate the size. */
bc.bpp = bc.endp = NULL;
bc.size = 0;
kret = krb5_krcc_unparse_offsets_internal(context, time_offset,
usec_offset, &bc);
CHECK_OUT(kret);

buf = malloc(bc.size);
if (buf == NULL)
return ENOMEM;

bc.bpp = buf;
bc.endp = buf + bc.size;
kret = krb5_krcc_unparse_offsets_internal(context, time_offset,
usec_offset, &bc);
CHECK(kret);

/* Success! */
*datapp = buf;
*lenptr = bc.bpp - buf;
kret = KRB5_OK;

errout:
if (kret)
free(buf);
return kret;
}
/*
* Utility routine: called by krb5_krcc_* functions to keep
* result of krb5_krcc_last_change_time up to date.
Expand Down

0 comments on commit fb4817a

Please sign in to comment.