Add the DIR ccache type
authorGreg Hudson <ghudson@mit.edu>
Mon, 5 Sep 2011 16:26:30 +0000 (16:26 +0000)
committerGreg Hudson <ghudson@mit.edu>
Mon, 5 Sep 2011 16:26:30 +0000 (16:26 +0000)
The DIR ccache type supports a collection of credential caches within
a private directory (which must be created out of band).  One cache is
designated as primary at any given time.  Setting the default cache
name to DIR:dirname will cause caches within dirname to be present in
the global cache collection.

ticket: 6953

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25154 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/krb5/ccache/Makefile.in
src/lib/krb5/ccache/cc-int.h
src/lib/krb5/ccache/cc_dir.c [new file with mode: 0644]
src/lib/krb5/ccache/cc_file.c
src/lib/krb5/ccache/ccbase.c
src/lib/krb5/ccache/fcc.h
src/lib/krb5/error_tables/k5e1_err.et

index 021bd760238ae458a111d701f82e84b32fd0f358..eaf0e00d43e3f076c1412fe2c76d907965f82a08 100644 (file)
@@ -24,6 +24,7 @@ STLIBOBJS= \
        cccursor.o \
        ccdefault.o \
        ccdefops.o \
+       cc_dir.o \
        cc_retr.o \
        cc_file.o \
        cc_memory.o \
@@ -36,6 +37,7 @@ OBJS= $(OUTPRE)ccbase.$(OBJEXT) \
        $(OUTPRE)cccursor.$(OBJEXT) \
        $(OUTPRE)ccdefault.$(OBJEXT) \
        $(OUTPRE)ccdefops.$(OBJEXT) \
+       $(OUTPRE)cc_dir.$(OBJEXT) \
        $(OUTPRE)cc_retr.$(OBJEXT) \
        $(OUTPRE)cc_file.$(OBJEXT) \
        $(OUTPRE)cc_memory.$(OBJEXT) \
@@ -48,6 +50,7 @@ SRCS= $(srcdir)/ccbase.c \
        $(srcdir)/cccursor.c \
        $(srcdir)/ccdefault.c \
        $(srcdir)/ccdefops.c \
+       $(srcdir)/cc_dir.c \
        $(srcdir)/cc_retr.c \
        $(srcdir)/cc_file.c \
        $(srcdir)/cc_memory.c \
index 97fed6362f605d84d8c0abd09473a9c79ba27e2f..aeb1447ce7172ddfbb8b3f6654a5cf083578d698 100644 (file)
@@ -112,4 +112,7 @@ k5_cc_mutex_force_unlock(k5_cc_mutex *m);
 krb5_error_code
 k5_cccol_force_unlock(void);
 
+krb5_error_code
+krb5int_fcc_new_unique(krb5_context context, char *template, krb5_ccache *id);
+
 #endif /* __KRB5_CCACHE_H__ */
diff --git a/src/lib/krb5/ccache/cc_dir.c b/src/lib/krb5/ccache/cc_dir.c
new file mode 100644 (file)
index 0000000..1f32c38
--- /dev/null
@@ -0,0 +1,703 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/ccache/cc_dir.c - Directory-based credential cache collection */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * This credential cache type represents a set of file-based caches with a
+ * switchable primary cache.  An alternate form of the type represents a
+ * subsidiary file cache within the directory.
+ *
+ * A cache name of the form DIR:dirname identifies a directory containing the
+ * cache set.  Resolving a name of this form results in dirname's primary
+ * cache.  If a context's default cache is of this form, the global cache
+ * collection will contain dirname's cache set, and new unique caches of type
+ * DIR will be created within dirname.
+ *
+ * A cache name of the form DIR::filepath represents a single cache within the
+ * directory.  Switching to a ccache of this type causes the directory's
+ * primary cache to be set to the named cache.
+ *
+ * Within the directory, cache names begin with 'tkt'.  The file "primary"
+ * contains a single line naming the primary cache.  The directory must already
+ * exist when the DIR ccache is resolved, but the primary file will be created
+ * automatically if it does not exist.
+ */
+
+#include "k5-int.h"
+#include "cc-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+/* This is Unix-only for now.  To work on Windows, we will need opendir/readdir
+ * replacements and possibly more flexible newline handling. */
+#ifndef _WIN32
+
+#include <dirent.h>
+
+extern const krb5_cc_ops krb5_dcc_ops;
+extern const krb5_cc_ops krb5_fcc_ops;
+
+/* Fields are not modified after creation, so no lock is necessary. */
+typedef struct dcc_data_st {
+    char *residual;             /* dirname or :filename */
+    krb5_ccache fcc;            /* File cache for actual cache ops */
+} dcc_data;
+
+static inline krb5_boolean
+filename_is_cache(const char *filename)
+{
+    return (strncmp(filename, "tkt", 3) == 0);
+}
+
+/* Compose the pathname of the primary file within a cache directory. */
+static inline krb5_error_code
+primary_pathname(const char *dirname, char **path_out)
+{
+    return k5_path_join(dirname, "primary", path_out);
+}
+
+/* Compose a residual string for a subsidiary path with the specified directory
+ * name and filename. */
+static krb5_error_code
+subsidiary_residual(const char *dirname, const char *filename, char **out)
+{
+    krb5_error_code ret;
+    char *path, *residual;
+
+    *out = NULL;
+    ret = k5_path_join(dirname, filename, &path);
+    if (ret)
+        return ret;
+    ret = asprintf(&residual, ":%s", path);
+    free(path);
+    if (ret < 0)
+        return ENOMEM;
+    *out = residual;
+    return 0;
+}
+
+static inline krb5_error_code
+split_path(krb5_context context, const char *path, char **dirname_out,
+           char **filename_out)
+{
+    krb5_error_code ret;
+    char *dirname, *filename;
+
+    *dirname_out = NULL;
+    *filename_out = NULL;
+    ret = k5_path_split(path, &dirname, &filename);
+    if (ret)
+        return ret;
+
+    if (*dirname == '\0') {
+        ret = KRB5_CC_BADNAME;
+        krb5_set_error_message(context, ret,
+                               _("Subsidiary cache path %s has no parent "
+                                 "directory"), path);
+        goto error;
+    }
+    if (!filename_is_cache(filename)) {
+        ret = KRB5_CC_BADNAME;
+        krb5_set_error_message(context, ret,
+                               _("Subsidiary cache path %s filename does not "
+                                 "begin with \"tkt\""), path);
+        goto error;
+    }
+
+    *dirname_out = dirname;
+    *filename_out = filename;
+    return 0;
+
+error:
+    free(dirname);
+    free(filename);
+    return ret;
+}
+
+/* Read the primary file and compose the residual string for the primary
+ * subsidiary cache file. */
+static krb5_error_code
+read_primary_file(krb5_context context, const char *primary_path,
+                  const char *dirname, char **residual_out)
+{
+    FILE *fp;
+    char buf[64], *ret;
+    size_t len;
+
+    *residual_out = NULL;
+
+    /* Open the file and read its first line. */
+    fp = fopen(primary_path, "r");
+    if (fp == NULL)
+        return ENOENT;
+    ret = fgets(buf, sizeof(buf), fp);
+    fclose(fp);
+    if (ret == NULL)
+        return KRB5_CC_IO;
+    len = strlen(buf);
+
+    /* Check if line is too long, doesn't look like a subsidiary cache
+     * filename, or isn't a single-component filename. */
+    if (buf[len - 1] != '\n' || !filename_is_cache(buf) ||
+        strchr(buf, '/') || strchr(buf, '\\')) {
+        krb5_set_error_message(context, KRB5_CC_FORMAT,
+                               _("%s contains invalid filename"),
+                               primary_path);
+        return KRB5_CC_FORMAT;
+    }
+    buf[len - 1] = '\0';
+
+    return subsidiary_residual(dirname, buf, residual_out);
+}
+
+/* Create or update the primary file with a line containing contents. */
+static krb5_error_code
+write_primary_file(const char *primary_path, const char *contents)
+{
+    krb5_error_code ret = KRB5_CC_IO;
+    char *newpath = NULL;
+    FILE *fp = NULL;
+    int fd = -1;
+
+    if (asprintf(&newpath, "%s.XXXXXX", primary_path) < 0)
+        return ENOMEM;
+    fd = mkstemp(newpath);
+    if (fd < 0)
+        goto cleanup;
+#ifdef HAVE_CHMOD
+    chmod(newpath, S_IRUSR | S_IWUSR);
+#endif
+    fp = fdopen(fd, "w");
+    if (fp == NULL)
+        goto cleanup;
+    fd = -1;
+    if (fprintf(fp, "%s\n", contents) < 0)
+        goto cleanup;
+    if (fclose(fp) == EOF)
+        goto cleanup;
+    fp = NULL;
+    if (rename(newpath, primary_path) != 0)
+        goto cleanup;
+    ret = 0;
+
+cleanup:
+    if (fd >= 0)
+        close(fd);
+    if (fp != NULL)
+        fclose(fp);
+    free(newpath);
+    return ret;
+}
+
+/* Verify that a cache directory path exists as a directory. */
+static krb5_error_code
+verify_dir(krb5_context context, const char *dirname)
+{
+    struct stat st;
+
+    if (stat(dirname, &st) < 0) {
+        krb5_set_error_message(context, KRB5_FCC_NOFILE,
+                               _("Credential cache directory %s does not "
+                                 "exist"), dirname);
+        return KRB5_FCC_NOFILE;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+        krb5_set_error_message(context, KRB5_CC_FORMAT,
+                               _("Credential cache directory %s exists but is"
+                                 "not a directory"), dirname);
+        return KRB5_CC_FORMAT;
+    }
+    return 0;
+}
+
+/*
+ * If the default ccache name for context is a directory collection, set
+ * *dirname_out to the directory name for that collection.  Otherwise set
+ * *dirname_out to NULL.
+ */
+static krb5_error_code
+get_context_default_dir(krb5_context context, char **dirname_out)
+{
+    const char *defname;
+    char *dirname;
+
+    *dirname_out = NULL;
+    defname = krb5_cc_default_name(context);
+    if (defname == NULL)
+        return 0;
+    if (strncmp(defname, "DIR:", 4) != 0 ||
+        defname[4] == ':' || defname[4] == '\0')
+        return 0;
+    dirname = strdup(defname + 4);
+    if (dirname == NULL)
+        return ENOMEM;
+    *dirname_out = dirname;
+    return 0;
+}
+
+static const char * KRB5_CALLCONV
+dcc_get_name(krb5_context context, krb5_ccache cache)
+{
+    dcc_data *data = cache->data;
+
+    return data->residual;
+}
+
+/* Construct a cache object given a residual string and file ccache.  Take
+ * ownership of fcc on success. */
+static krb5_error_code
+make_cache(const char *residual, krb5_ccache fcc, krb5_ccache *cache_out)
+{
+    krb5_ccache cache = NULL;
+    dcc_data *data = NULL;
+    char *residual_copy = NULL;
+
+    cache = malloc(sizeof(*cache));
+    if (cache == NULL)
+        goto oom;
+    data = malloc(sizeof(*data));
+    if (data == NULL)
+        goto oom;
+    residual_copy = strdup(residual);
+    if (residual_copy == NULL)
+        goto oom;
+
+    data->residual = residual_copy;
+    data->fcc = fcc;
+    cache->ops = &krb5_dcc_ops;
+    cache->data = data;
+    cache->magic = KV5M_CCACHE;
+    *cache_out = cache;
+    return 0;
+
+oom:
+    free(cache);
+    free(data);
+    free(residual_copy);
+    return ENOMEM;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_resolve(krb5_context context, krb5_ccache *cache_out, const char *residual)
+{
+    krb5_error_code ret;
+    krb5_ccache fcc;
+    char *primary_path = NULL, *sresidual = NULL, *dirname, *filename;
+
+    *cache_out = NULL;
+
+    if (*residual == ':') {
+        /* This is a subsidiary cache within the directory. */
+        ret = split_path(context, residual + 1, &dirname, &filename);
+        if (ret)
+            return ret;
+
+        ret = verify_dir(context, dirname);
+        free(dirname);
+        free(filename);
+        if (ret)
+            return ret;
+    } else {
+        /* This is the directory itself; resolve to the primary cache. */
+        ret = verify_dir(context, residual);
+        if (ret)
+            return ret;
+
+        ret = primary_pathname(residual, &primary_path);
+        if (ret)
+            goto cleanup;
+
+        ret = read_primary_file(context, primary_path, residual, &sresidual);
+        if (ret == ENOENT) {
+            /* Create an initial primary file. */
+            ret = write_primary_file(primary_path, "tkt");
+            if (ret)
+                goto cleanup;
+            ret = subsidiary_residual(residual, "tkt", &sresidual);
+            if (ret)
+                goto cleanup;
+        }
+        residual = sresidual;
+    }
+
+    ret = krb5_fcc_ops.resolve(context, &fcc, residual + 1);
+    if (ret)
+        goto cleanup;
+    ret = make_cache(residual, fcc, cache_out);
+    if (ret)
+        krb5_fcc_ops.close(context, fcc);
+
+cleanup:
+    free(primary_path);
+    free(sresidual);
+    return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_gen_new(krb5_context context, krb5_ccache *cache_out)
+{
+    krb5_error_code ret;
+    char *dirname = NULL, *template = NULL, *residual = NULL;
+    krb5_ccache fcc;
+
+    *cache_out = NULL;
+    ret = get_context_default_dir(context, &dirname);
+    if (ret)
+        return ret;
+    if (dirname == NULL) {
+        krb5_set_error_message(context, KRB5_DCC_CANNOT_CREATE,
+                               _("Can't create new subsidiary cache because "
+                                 "default cache is not a directory "
+                                 "collection"));
+        return KRB5_DCC_CANNOT_CREATE;
+    }
+    ret = k5_path_join(dirname, "tktXXXXXX", &template);
+    if (ret)
+        goto cleanup;
+    ret = krb5int_fcc_new_unique(context, template, &fcc);
+    if (ret)
+        goto cleanup;
+    if (asprintf(&residual, ":%s", template) < 0) {
+        ret = ENOMEM;
+        goto cleanup;
+    }
+    ret = make_cache(residual, fcc, cache_out);
+    if (ret)
+        krb5_fcc_ops.destroy(context, fcc);
+
+cleanup:
+    free(dirname);
+    free(template);
+    free(residual);
+    return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_init(krb5_context context, krb5_ccache cache, krb5_principal princ)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.init(context, data->fcc, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_destroy(krb5_context context, krb5_ccache cache)
+{
+    dcc_data *data = cache->data;
+    krb5_error_code ret;
+
+    ret = krb5_fcc_ops.destroy(context, data->fcc);
+    free(data->residual);
+    free(data);
+    return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_close(krb5_context context, krb5_ccache cache)
+{
+    dcc_data *data = cache->data;
+    krb5_error_code ret;
+
+    ret = krb5_fcc_ops.close(context, data->fcc);
+    free(data->residual);
+    free(data);
+    return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_store(krb5_context context, krb5_ccache cache, krb5_creds *creds)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.store(context, data->fcc, creds);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_retrieve(krb5_context context, krb5_ccache cache, krb5_flags flags,
+             krb5_creds *mcreds, krb5_creds *creds)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.retrieve(context, data->fcc, flags, mcreds,
+                                      creds);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_princ(krb5_context context, krb5_ccache cache,
+              krb5_principal *princ_out)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.get_princ(context, data->fcc, princ_out);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_first(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.get_first(context, data->fcc, cursor);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_next(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
+             krb5_creds *creds)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.get_next(context, data->fcc, cursor, creds);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_end_get(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.end_get(context, data->fcc, cursor);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
+                krb5_creds *creds)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.remove_cred(context, data->fcc, flags, creds);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_set_flags(krb5_context context, krb5_ccache cache, krb5_flags flags)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.set_flags(context, data->fcc, flags);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_flags(krb5_context context, krb5_ccache cache, krb5_flags *flags_out)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.get_flags(context, data->fcc, flags_out);
+}
+
+struct dcc_ptcursor_data {
+    char *primary;
+    char *dirname;
+    DIR *dir;
+    krb5_boolean first;
+};
+
+/* Construct a cursor, taking ownership of dirname, primary, and dir on
+ * success. */
+static krb5_error_code
+make_cursor(char *dirname, char *primary, DIR *dir,
+            krb5_cc_ptcursor *cursor_out)
+{
+    krb5_cc_ptcursor cursor;
+    struct dcc_ptcursor_data *data;
+
+    *cursor_out = NULL;
+
+    data = malloc(sizeof(*data));
+    if (data == NULL)
+        return ENOMEM;
+    cursor = malloc(sizeof(*cursor));
+    if (cursor == NULL) {
+        free(data);
+        return ENOMEM;
+    }
+
+    data->dirname = dirname;
+    data->primary = primary;
+    data->dir = dir;
+    data->first = TRUE;
+    cursor->ops = &krb5_dcc_ops;
+    cursor->data = data;
+    *cursor_out = cursor;
+    return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
+{
+    krb5_error_code ret;
+    char *dirname = NULL, *primary_path = NULL, *primary = NULL;
+    DIR *dir = NULL;
+
+    *cursor_out = NULL;
+
+    /* Open the directory for the context's default cache. */
+    ret = get_context_default_dir(context, &dirname);
+    if (ret || dirname == NULL)
+        goto cleanup;
+    dir = opendir(dirname);
+    if (dir == NULL)
+        goto cleanup;
+
+    /* Fetch the primary cache name if possible. */
+    ret = primary_pathname(dirname, &primary_path);
+    if (ret)
+        goto cleanup;
+    ret = read_primary_file(context, primary_path, dirname, &primary);
+    if (ret)
+        krb5_clear_error_message(context);
+
+    ret = make_cursor(dirname, primary, dir, cursor_out);
+    if (ret)
+        goto cleanup;
+    dirname = primary = NULL;
+    dir = NULL;
+
+cleanup:
+    free(dirname);
+    free(primary_path);
+    free(primary);
+    if (dir)
+        closedir(dir);
+    /* Return an empty cursor if we fail for any reason. */
+    if (*cursor_out == NULL)
+        return make_cursor(NULL, NULL, NULL, cursor_out);
+    return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
+                  krb5_ccache *cache_out)
+{
+    struct dcc_ptcursor_data *data = cursor->data;
+    struct dirent *ent;
+    char *residual;
+    krb5_error_code ret;
+    struct stat sb;
+
+    *cache_out = NULL;
+    if (data->dir == NULL)      /* Empty cursor */
+        return 0;
+
+    /* Return the primary cache if we haven't yet. */
+    if (data->first) {
+        data->first = FALSE;
+        if (data->primary != NULL && stat(data->primary + 1, &sb) == 0)
+            return dcc_resolve(context, cache_out, data->primary);
+    }
+
+    /* Look for the next filename of the correct form, without repeating the
+     * primary cache. */
+    while ((ent = readdir(data->dir)) != NULL) {
+        if (!filename_is_cache(ent->d_name))
+            continue;
+        ret = subsidiary_residual(data->dirname, ent->d_name, &residual);
+        if (ret)
+            return ret;
+        if (data->primary != NULL && strcmp(residual, data->primary) == 0) {
+            free(residual);
+            continue;
+        }
+        ret = dcc_resolve(context, cache_out, residual);
+        free(residual);
+        return ret;
+    }
+
+    /* We exhausted the directory without finding a cache to yield. */
+    free(data->dir);
+    data->dir = NULL;
+    return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
+{
+    struct dcc_ptcursor_data *data = (*cursor)->data;
+
+    if (data->dir)
+        closedir(data->dir);
+    free(data->dirname);
+    free(data->primary);
+    free(data);
+    free(*cursor);
+    *cursor = NULL;
+    return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_lastchange(krb5_context context, krb5_ccache cache,
+               krb5_timestamp *time_out)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.lastchange(context, data->fcc, time_out);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_lock(krb5_context context, krb5_ccache cache)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.lock(context, data->fcc);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_unlock(krb5_context context, krb5_ccache cache)
+{
+    dcc_data *data = cache->data;
+
+    return krb5_fcc_ops.unlock(context, data->fcc);
+}
+
+const krb5_cc_ops krb5_dcc_ops = {
+    0,
+    "DIR",
+    dcc_get_name,
+    dcc_resolve,
+    dcc_gen_new,
+    dcc_init,
+    dcc_destroy,
+    dcc_close,
+    dcc_store,
+    dcc_retrieve,
+    dcc_get_princ,
+    dcc_get_first,
+    dcc_get_next,
+    dcc_end_get,
+    dcc_remove_cred,
+    dcc_set_flags,
+    dcc_get_flags,
+    dcc_ptcursor_new,
+    dcc_ptcursor_next,
+    dcc_ptcursor_free,
+    NULL, /* move */
+    dcc_lastchange,
+    NULL, /* wasdefault */
+    dcc_lock,
+    dcc_unlock,
+};
+
+#endif /* not _WIN32 */
index 131ccb533dc42288a8ad9e7e74bc7dc3424430ca..aee8a8396933aa6ccdb554f60ef0bb83b1ad55ff 100644 (file)
@@ -1945,29 +1945,14 @@ krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *curso
     return 0;
 }
 
-
-/*
- * Effects:
- * Creates a new file cred cache whose name is guaranteed to be
- * unique.  The name begins with the string TKT_ROOT (from fcc.h).
- * The cache is not opened, but the new filename is reserved.
- *
- * Returns:
- * The filled in krb5_ccache id.
- *
- * Errors:
- * KRB5_CC_NOMEM - there was insufficient memory to allocate the
- *              krb5_ccache.  id is undefined.
- * system errors (from open)
- */
-static krb5_error_code KRB5_CALLCONV
-krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
+/* Generate a unique file ccache using the given template (which will be
+ * modified to contain the actual name of the file). */
+krb5_error_code
+krb5int_fcc_new_unique(krb5_context context, char *template, krb5_ccache *id)
 {
     krb5_ccache lid;
     int ret;
     krb5_error_code    kret = 0;
-    char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for
-                                           NUL */
     krb5_fcc_data *data;
     krb5_int16 fcc_fvno = htons(context->fcc_default_format);
     krb5_int16 fcc_flen = 0;
@@ -1979,8 +1964,7 @@ krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
     if (kret)
         return kret;
 
-    (void) snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
-    ret = mkstemp(scratch);
+    ret = mkstemp(template);
     if (ret == -1) {
         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
         return krb5_fcc_interpret(context, errno);
@@ -1992,16 +1976,16 @@ krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
     if (data == NULL) {
         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
         close(ret);
-        unlink(scratch);
+        unlink(template);
         return KRB5_CC_NOMEM;
     }
 
-    data->filename = strdup(scratch);
+    data->filename = strdup(template);
     if (data->filename == NULL) {
         k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
         free(data);
         close(ret);
-        unlink(scratch);
+        unlink(template);
         return KRB5_CC_NOMEM;
     }
 
@@ -2011,7 +1995,7 @@ krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
         free(data->filename);
         free(data);
         close(ret);
-        unlink(scratch);
+        unlink(template);
         return kret;
     }
     kret = k5_cc_mutex_lock(context, &data->lock);
@@ -2021,7 +2005,7 @@ krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
         free(data->filename);
         free(data);
         close(ret);
-        unlink(scratch);
+        unlink(template);
         return kret;
     }
 
@@ -2078,7 +2062,7 @@ krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
         free(data->filename);
         free(data);
         (void) close(ret);
-        (void) unlink(scratch);
+        (void) unlink(template);
         return KRB5_CC_NOMEM;
     }
     setptr->refcount = 1;
@@ -2118,6 +2102,30 @@ err_out:
     return kret;
 }
 
+/*
+ * Effects:
+ * Creates a new file cred cache whose name is guaranteed to be
+ * unique.  The name begins with the string TKT_ROOT (from fcc.h).
+ * The cache is not opened, but the new filename is reserved.
+ *
+ * Returns:
+ * The filled in krb5_ccache id.
+ *
+ * Errors:
+ * KRB5_CC_NOMEM - there was insufficient memory to allocate the
+ *              krb5_ccache.  id is undefined.
+ * system errors (from open)
+ */
+static krb5_error_code KRB5_CALLCONV
+krb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
+{
+    char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for
+                                           NUL */
+
+    (void) snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
+    return krb5int_fcc_new_unique(context, scratch, id);
+}
+
 /*
  * Requires:
  * id is a file credential cache
index bd54695c84fcec81244ddb454a91aa59df0430cd..b330784c90664ab251b75ef15b030c1e41636764 100644 (file)
@@ -75,6 +75,13 @@ static struct krb5_cc_typelist cc_krcc_entry = { &krb5_krcc_ops, NEXT };
 #define NEXT &cc_krcc_entry
 #endif /* USE_KEYRING_CCACHE */
 
+#ifndef _WIN32
+extern const krb5_cc_ops krb5_dcc_ops;
+static struct krb5_cc_typelist cc_dcc_entry = { &krb5_dcc_ops, NEXT };
+#undef NEXT
+#define NEXT &cc_dcc_entry
+#endif /* not _WIN32 */
+
 
 #define INITIAL_TYPEHEAD (NEXT)
 static struct krb5_cc_typelist *cc_typehead = INITIAL_TYPEHEAD;
index 7524e12d8d9d2c7f5650a8edfb84bc718b6e4d91..b31c3a7af7ed9dfc043a4183446f8c5ae2b4a8e5 100644 (file)
@@ -35,4 +35,7 @@
 
 extern const krb5_cc_ops krb5_cc_file_ops;
 
+krb5_error_code krb5int_fcc_new_unique(krb5_context context, char *template,
+                                       krb5_ccache *id);
+
 #endif /* __KRB5_FILE_CCACHE__ */
index af2871049845188da63bc61839fb4904b7819925..98374ed52588b1cc080c709e19e5f233be71ecb9 100644 (file)
@@ -34,4 +34,5 @@ error_code KRB5_PLUGIN_VER_NOTSUPP, "Plugin does not support interface version"
 error_code KRB5_PLUGIN_BAD_MODULE_SPEC, "Invalid module specifier"
 error_code KRB5_PLUGIN_NAME_NOTFOUND, "Plugin module name not found"
 error_code KRB5KDC_ERR_DISCARD, "The KDC should discard this request"
+error_code KRB5_DCC_CANNOT_CREATE, "Can't create new subsidiary cache"
 end