From 319c01a8f523843169b9e5342ac2d085ad67f8a2 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Mon, 5 Sep 2011 16:26:30 +0000 Subject: [PATCH] Add the DIR ccache type 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 | 3 + src/lib/krb5/ccache/cc-int.h | 3 + src/lib/krb5/ccache/cc_dir.c | 703 ++++++++++++++++++++++++++ src/lib/krb5/ccache/cc_file.c | 62 ++- src/lib/krb5/ccache/ccbase.c | 7 + src/lib/krb5/ccache/fcc.h | 3 + src/lib/krb5/error_tables/k5e1_err.et | 1 + 7 files changed, 755 insertions(+), 27 deletions(-) create mode 100644 src/lib/krb5/ccache/cc_dir.c diff --git a/src/lib/krb5/ccache/Makefile.in b/src/lib/krb5/ccache/Makefile.in index 021bd7602..eaf0e00d4 100644 --- a/src/lib/krb5/ccache/Makefile.in +++ b/src/lib/krb5/ccache/Makefile.in @@ -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 \ diff --git a/src/lib/krb5/ccache/cc-int.h b/src/lib/krb5/ccache/cc-int.h index 97fed6362..aeb1447ce 100644 --- a/src/lib/krb5/ccache/cc-int.h +++ b/src/lib/krb5/ccache/cc-int.h @@ -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 index 000000000..1f32c389a --- /dev/null +++ b/src/lib/krb5/ccache/cc_dir.c @@ -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 +#endif +#if HAVE_SYS_STAT_H +#include +#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 + +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 */ diff --git a/src/lib/krb5/ccache/cc_file.c b/src/lib/krb5/ccache/cc_file.c index 131ccb533..aee8a8396 100644 --- a/src/lib/krb5/ccache/cc_file.c +++ b/src/lib/krb5/ccache/cc_file.c @@ -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 diff --git a/src/lib/krb5/ccache/ccbase.c b/src/lib/krb5/ccache/ccbase.c index bd54695c8..b330784c9 100644 --- a/src/lib/krb5/ccache/ccbase.c +++ b/src/lib/krb5/ccache/ccbase.c @@ -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; diff --git a/src/lib/krb5/ccache/fcc.h b/src/lib/krb5/ccache/fcc.h index 7524e12d8..b31c3a7af 100644 --- a/src/lib/krb5/ccache/fcc.h +++ b/src/lib/krb5/ccache/fcc.h @@ -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__ */ diff --git a/src/lib/krb5/error_tables/k5e1_err.et b/src/lib/krb5/error_tables/k5e1_err.et index af2871049..98374ed52 100644 --- a/src/lib/krb5/error_tables/k5e1_err.et +++ b/src/lib/krb5/error_tables/k5e1_err.et @@ -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 -- 2.26.2