2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/ccache/cccursor.c
2N/A *
2N/A * Copyright 2006, 2007 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A * cursor for sequential traversal of ccaches
2N/A */
2N/A
2N/A#include "cc-int.h"
2N/A
2N/A#include <assert.h>
2N/A
2N/A#define CCCURSOR_CONTEXT 1
2N/A#define CCCURSOR_ENV 2
2N/A#define CCCURSOR_OS 3
2N/A#define CCCURSOR_PERTYPE 4
2N/A
2N/A#define NFULLNAMES 3
2N/A
2N/A/* Prefix and residual parts of a full ccache name. */
2N/Astruct cc_fullname {
2N/A char *pfx;
2N/A char *res;
2N/A};
2N/A
2N/Astruct _krb5_cccol_cursor {
2N/A int pos;
2N/A krb5_cc_typecursor typecursor;
2N/A const krb5_cc_ops *ops;
2N/A krb5_cc_ptcursor ptcursor;
2N/A int cur_fullname;
2N/A struct cc_fullname fullnames[NFULLNAMES]; /* previously seen ccaches */
2N/A};
2N/A/* typedef of krb5_cccol_cursor is in krb5.h */
2N/A
2N/Astatic int cccol_already(krb5_context, krb5_cccol_cursor, krb5_ccache *);
2N/A
2N/Astatic int cccol_cmpname(const char *, const char *, struct cc_fullname *);
2N/A
2N/Astatic krb5_error_code
2N/Acccol_do_resolve(krb5_context, krb5_cccol_cursor, const char *, krb5_ccache *);
2N/A
2N/Astatic krb5_error_code
2N/Acccol_pertype_next(krb5_context, krb5_cccol_cursor, krb5_ccache *);
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_cccol_cursor_new(krb5_context context,
2N/A krb5_cccol_cursor *cursor)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_cccol_cursor n = NULL;
2N/A int i;
2N/A
2N/A *cursor = NULL;
2N/A n = malloc(sizeof(*n));
2N/A if (n == NULL)
2N/A return ENOMEM;
2N/A
2N/A n->pos = CCCURSOR_CONTEXT;
2N/A n->typecursor = NULL;
2N/A n->ptcursor = NULL;
2N/A n->ops = NULL;
2N/A
2N/A for (i = 0; i < NFULLNAMES; i++) {
2N/A n->fullnames[i].pfx = n->fullnames[i].res = NULL;
2N/A }
2N/A n->cur_fullname = 0;
2N/A ret = krb5int_cc_typecursor_new(context, &n->typecursor);
2N/A if (ret)
2N/A goto errout;
2N/A
2N/A do {
2N/A /* Find first backend with ptcursor functionality. */
2N/A ret = krb5int_cc_typecursor_next(context, n->typecursor, &n->ops);
2N/A if (ret || n->ops == NULL)
2N/A goto errout;
2N/A } while (n->ops->ptcursor_new == NULL);
2N/A
2N/A ret = n->ops->ptcursor_new(context, &n->ptcursor);
2N/A if (ret)
2N/A goto errout;
2N/A
2N/Aerrout:
2N/A if (ret) {
2N/A krb5_cccol_cursor_free(context, &n);
2N/A }
2N/A *cursor = n;
2N/A return ret;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_cccol_cursor_next(krb5_context context,
2N/A krb5_cccol_cursor cursor,
2N/A krb5_ccache *ccache)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A char *name;
2N/A krb5_os_context os_ctx = NULL;
2N/A
2N/A *ccache = NULL;
2N/A /* Solaris Kerberos */
2N/A os_ctx = &context->os_context;
2N/A
2N/A switch (cursor->pos) {
2N/A case CCCURSOR_CONTEXT:
2N/A name = os_ctx->default_ccname;
2N/A if (name != NULL) {
2N/A cursor->pos = CCCURSOR_ENV;
2N/A ret = cccol_do_resolve(context, cursor, name, ccache);
2N/A if (ret)
2N/A goto errout;
2N/A if (*ccache != NULL)
2N/A break;
2N/A }
2N/A /* fall through */
2N/A case CCCURSOR_ENV:
2N/A name = getenv(KRB5_ENV_CCNAME);
2N/A if (name != NULL) {
2N/A cursor->pos = CCCURSOR_OS;
2N/A ret = cccol_do_resolve(context, cursor, name, ccache);
2N/A if (ret)
2N/A goto errout;
2N/A if (*ccache != NULL)
2N/A break;
2N/A }
2N/A /* fall through */
2N/A case CCCURSOR_OS:
2N/A ret = krb5int_cc_os_default_name(context, &name);
2N/A if (ret) goto errout;
2N/A if (name != NULL) {
2N/A cursor->pos = CCCURSOR_PERTYPE;
2N/A ret = cccol_do_resolve(context, cursor, name, ccache);
2N/A free(name);
2N/A if (ret)
2N/A goto errout;
2N/A if (*ccache != NULL)
2N/A break;
2N/A }
2N/A /* fall through */
2N/A case CCCURSOR_PERTYPE:
2N/A cursor->pos = CCCURSOR_PERTYPE;
2N/A do {
2N/A ret = cccol_pertype_next(context, cursor, ccache);
2N/A if (ret)
2N/A goto errout;
2N/A } while (cccol_already(context, cursor, ccache));
2N/A break;
2N/A }
2N/Aerrout:
2N/A return ret;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_cccol_cursor_free(krb5_context context,
2N/A krb5_cccol_cursor *cursor)
2N/A{
2N/A krb5_cccol_cursor c = *cursor;
2N/A int i;
2N/A
2N/A if (c == NULL)
2N/A return 0;
2N/A
2N/A for (i = 0; i < NFULLNAMES; i++) {
2N/A if (c->fullnames[i].pfx != NULL)
2N/A free(c->fullnames[i].pfx);
2N/A if (c->fullnames[i].res != NULL)
2N/A free(c->fullnames[i].res);
2N/A }
2N/A if (c->ptcursor != NULL)
2N/A c->ops->ptcursor_free(context, &c->ptcursor);
2N/A if (c->typecursor != NULL)
2N/A krb5int_cc_typecursor_free(context, &c->typecursor);
2N/A free(c);
2N/A
2N/A *cursor = NULL;
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_cccol_last_change_time(krb5_context context,
2N/A krb5_timestamp *change_time)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_cccol_cursor c = NULL;
2N/A krb5_ccache ccache = NULL;
2N/A krb5_timestamp last_time = 0;
2N/A krb5_timestamp max_change_time = 0;
2N/A
2N/A *change_time = 0;
2N/A
2N/A ret = krb5_cccol_cursor_new(context, &c);
2N/A
2N/A while (!ret) {
2N/A ret = krb5_cccol_cursor_next(context, c, &ccache);
2N/A if (ccache) {
2N/A ret = krb5_cc_last_change_time(context, ccache, &last_time);
2N/A if (!ret && last_time > max_change_time) {
2N/A max_change_time = last_time;
2N/A }
2N/A ret = 0;
2N/A }
2N/A else {
2N/A break;
2N/A }
2N/A }
2N/A *change_time = max_change_time;
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * krb5_cccol_lock and krb5_cccol_unlock are defined in ccbase.c
2N/A */
2N/A
2N/A/*
2N/A * Determine if a ccache from a per-type cursor was already one of the
2N/A * higher-priority defaults.
2N/A */
2N/Astatic int
2N/Acccol_already(krb5_context context,
2N/A krb5_cccol_cursor c,
2N/A krb5_ccache *ccache)
2N/A{
2N/A const char *name = NULL, *prefix = NULL;
2N/A int i;
2N/A
2N/A if (*ccache == NULL)
2N/A return 0;
2N/A name = krb5_cc_get_name(context, *ccache);
2N/A if (name == NULL)
2N/A return 0;
2N/A prefix = krb5_cc_get_type(context, *ccache);
2N/A
2N/A assert(c->cur_fullname < NFULLNAMES);
2N/A for (i = 0; i < c->cur_fullname; i++) {
2N/A if (cccol_cmpname(prefix, name, &c->fullnames[i])) {
2N/A krb5_cc_close(context, *ccache);
2N/A *ccache = NULL;
2N/A return 1;
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Compare {prefix, name} against a cc_fullname.
2N/A */
2N/Astatic int
2N/Acccol_cmpname(const char *prefix,
2N/A const char *name,
2N/A struct cc_fullname *fullname)
2N/A{
2N/A if (fullname->pfx == NULL || fullname->res == NULL)
2N/A return 0;
2N/A if (strcmp(prefix, fullname->pfx))
2N/A return 0;
2N/A if (strcmp(name, fullname->res))
2N/A return 0;
2N/A
2N/A return 1;
2N/A}
2N/A
2N/A/*
2N/A * Resolve one of the high-precedence ccaches, and cache its full name
2N/A * {prefix, residual} for exclusion when doing per-type ccache
2N/A * iteration. Also check to see if we've already seen the ccache
2N/A * name we're given.
2N/A */
2N/Astatic krb5_error_code
2N/Acccol_do_resolve(krb5_context context,
2N/A krb5_cccol_cursor cursor,
2N/A const char *name,
2N/A krb5_ccache *ccache)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A struct cc_fullname *fullname;
2N/A
2N/A assert(cursor->cur_fullname < NFULLNAMES);
2N/A ret = krb5_cc_resolve(context, name, ccache);
2N/A if (ret)
2N/A return ret;
2N/A
2N/A if (cccol_already(context, cursor, ccache))
2N/A return 0;
2N/A
2N/A fullname = &cursor->fullnames[cursor->cur_fullname];
2N/A fullname->pfx = strdup(krb5_cc_get_type(context, *ccache));
2N/A fullname->res = strdup(krb5_cc_get_name(context, *ccache));
2N/A cursor->cur_fullname++;
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Find next ccache in current backend, iterating through backends if
2N/A * ccache list of the current backend is exhausted.
2N/A */
2N/Astatic krb5_error_code
2N/Acccol_pertype_next(krb5_context context,
2N/A krb5_cccol_cursor cursor,
2N/A krb5_ccache *ccache)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A
2N/A *ccache = NULL;
2N/A
2N/A /* Are we out of backends? */
2N/A if (cursor->ops == NULL)
2N/A return 0;
2N/A /*
2N/A * Loop in case there are multiple backends with empty ccache
2N/A * lists.
2N/A */
2N/A while (*ccache == NULL) {
2N/A ret = cursor->ops->ptcursor_next(context, cursor->ptcursor, ccache);
2N/A if (ret)
2N/A goto errout;
2N/A if (*ccache != NULL)
2N/A return 0;
2N/A
2N/A ret = cursor->ops->ptcursor_free(context, &cursor->ptcursor);
2N/A if (ret)
2N/A goto errout;
2N/A
2N/A do {
2N/A /* Find first backend with ptcursor functionality. */
2N/A ret = krb5int_cc_typecursor_next(context, cursor->typecursor,
2N/A &cursor->ops);
2N/A if (ret)
2N/A goto errout;
2N/A if (cursor->ops == NULL)
2N/A return 0;
2N/A } while (cursor->ops->ptcursor_new == NULL);
2N/A
2N/A ret = cursor->ops->ptcursor_new(context, &cursor->ptcursor);
2N/A if (ret)
2N/A goto errout;
2N/A }
2N/Aerrout:
2N/A return ret;
2N/A}