1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_dir.c - Directory-based credential cache collection */
3 /*
4 * Copyright (C) 2011 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 /*
28 * This credential cache type represents a set of file-based caches with a
29 * switchable primary cache. An alternate form of the type represents a
30 * subsidiary file cache within the directory.
31 *
32 * A cache name of the form DIR:dirname identifies a directory containing the
33 * cache set. Resolving a name of this form results in dirname's primary
34 * cache. If a context's default cache is of this form, the global cache
35 * collection will contain dirname's cache set, and new unique caches of type
36 * DIR will be created within dirname.
37 *
38 * A cache name of the form DIR::filepath represents a single cache within the
39 * directory. Switching to a ccache of this type causes the directory's
40 * primary cache to be set to the named cache.
41 *
42 * Within the directory, cache names begin with 'tkt'. The file "primary"
43 * contains a single line naming the primary cache. The directory must already
44 * exist when the DIR ccache is resolved, but the primary file will be created
45 * automatically if it does not exist.
46 */
47
48 #include "k5-int.h"
49 #include "cc-int.h"
50
51 #if HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 #if HAVE_SYS_STAT_H
55 #include <sys/stat.h>
56 #endif
57
58 /* This is Unix-only for now. To work on Windows, we will need opendir/readdir
59 * replacements and possibly more flexible newline handling. */
60 #ifndef _WIN32
61
62 #include <dirent.h>
63
64 extern const krb5_cc_ops krb5_dcc_ops;
65 extern const krb5_cc_ops krb5_fcc_ops;
66
67 /* Fields are not modified after creation, so no lock is necessary. */
68 typedef struct dcc_data_st {
69 char *residual; /* dirname or :filename */
70 krb5_ccache fcc; /* File cache for actual cache ops */
71 } dcc_data;
72
73 static inline krb5_boolean
filename_is_cache(const char * filename)74 filename_is_cache(const char *filename)
75 {
76 return (strncmp(filename, "tkt", 3) == 0);
77 }
78
79 /* Compose the pathname of the primary file within a cache directory. */
80 static inline krb5_error_code
primary_pathname(const char * dirname,char ** path_out)81 primary_pathname(const char *dirname, char **path_out)
82 {
83 return k5_path_join(dirname, "primary", path_out);
84 }
85
86 /* Compose a residual string for a subsidiary path with the specified directory
87 * name and filename. */
88 static krb5_error_code
subsidiary_residual(const char * dirname,const char * filename,char ** out)89 subsidiary_residual(const char *dirname, const char *filename, char **out)
90 {
91 krb5_error_code ret;
92 char *path, *residual;
93
94 *out = NULL;
95 ret = k5_path_join(dirname, filename, &path);
96 if (ret)
97 return ret;
98 ret = asprintf(&residual, ":%s", path);
99 free(path);
100 if (ret < 0)
101 return ENOMEM;
102 *out = residual;
103 return 0;
104 }
105
106 static inline krb5_error_code
split_path(krb5_context context,const char * path,char ** dirname_out,char ** filename_out)107 split_path(krb5_context context, const char *path, char **dirname_out,
108 char **filename_out)
109 {
110 krb5_error_code ret;
111 char *dirname, *filename;
112
113 *dirname_out = NULL;
114 *filename_out = NULL;
115 ret = k5_path_split(path, &dirname, &filename);
116 if (ret)
117 return ret;
118
119 if (*dirname == '\0') {
120 ret = KRB5_CC_BADNAME;
121 k5_setmsg(context, ret,
122 _("Subsidiary cache path %s has no parent directory"), path);
123 goto error;
124 }
125 if (!filename_is_cache(filename)) {
126 ret = KRB5_CC_BADNAME;
127 k5_setmsg(context, ret,
128 _("Subsidiary cache path %s filename does not begin with "
129 "\"tkt\""), path);
130 goto error;
131 }
132
133 *dirname_out = dirname;
134 *filename_out = filename;
135 return 0;
136
137 error:
138 free(dirname);
139 free(filename);
140 return ret;
141 }
142
143 /* Read the primary file and compose the residual string for the primary
144 * subsidiary cache file. */
145 static krb5_error_code
read_primary_file(krb5_context context,const char * primary_path,const char * dirname,char ** residual_out)146 read_primary_file(krb5_context context, const char *primary_path,
147 const char *dirname, char **residual_out)
148 {
149 FILE *fp;
150 char buf[64], *ret;
151 size_t len;
152
153 *residual_out = NULL;
154
155 /* Open the file and read its first line. */
156 fp = fopen(primary_path, "r");
157 if (fp == NULL)
158 return ENOENT;
159 ret = fgets(buf, sizeof(buf), fp);
160 fclose(fp);
161 if (ret == NULL)
162 return KRB5_CC_IO;
163 len = strlen(buf);
164
165 /* Check if line is too long, doesn't look like a subsidiary cache
166 * filename, or isn't a single-component filename. */
167 if (buf[len - 1] != '\n' || !filename_is_cache(buf) ||
168 strchr(buf, '/') || strchr(buf, '\\')) {
169 k5_setmsg(context, KRB5_CC_FORMAT, _("%s contains invalid filename"),
170 primary_path);
171 return KRB5_CC_FORMAT;
172 }
173 buf[len - 1] = '\0';
174
175 return subsidiary_residual(dirname, buf, residual_out);
176 }
177
178 /* Create or update the primary file with a line containing contents. */
179 static krb5_error_code
write_primary_file(const char * primary_path,const char * contents)180 write_primary_file(const char *primary_path, const char *contents)
181 {
182 krb5_error_code ret = KRB5_CC_IO;
183 char *newpath = NULL;
184 FILE *fp = NULL;
185 int fd = -1, status;
186
187 if (asprintf(&newpath, "%s.XXXXXX", primary_path) < 0)
188 return ENOMEM;
189 fd = mkstemp(newpath);
190 if (fd < 0)
191 goto cleanup;
192 #ifdef HAVE_CHMOD
193 chmod(newpath, S_IRUSR | S_IWUSR);
194 #endif
195 fp = fdopen(fd, "w");
196 if (fp == NULL)
197 goto cleanup;
198 fd = -1;
199 if (fprintf(fp, "%s\n", contents) < 0)
200 goto cleanup;
201 status = fclose(fp);
202 fp = NULL;
203 if (status == EOF)
204 goto cleanup;
205 fp = NULL;
206 if (rename(newpath, primary_path) != 0)
207 goto cleanup;
208 ret = 0;
209
210 cleanup:
211 if (fd >= 0)
212 close(fd);
213 if (fp != NULL)
214 fclose(fp);
215 free(newpath);
216 return ret;
217 }
218
219 /* Verify or create a cache directory path. */
220 static krb5_error_code
verify_dir(krb5_context context,const char * dirname)221 verify_dir(krb5_context context, const char *dirname)
222 {
223 struct stat st;
224
225 if (stat(dirname, &st) < 0) {
226 if (errno == ENOENT && mkdir(dirname, S_IRWXU) == 0)
227 return 0;
228 k5_setmsg(context, KRB5_FCC_NOFILE,
229 _("Credential cache directory %s does not exist"),
230 dirname);
231 return KRB5_FCC_NOFILE;
232 }
233 if (!S_ISDIR(st.st_mode)) {
234 k5_setmsg(context, KRB5_CC_FORMAT,
235 _("Credential cache directory %s exists but is not a "
236 "directory"), dirname);
237 return KRB5_CC_FORMAT;
238 }
239 return 0;
240 }
241
242 /*
243 * If the default ccache name for context is a directory collection, set
244 * *dirname_out to the directory name for that collection. Otherwise set
245 * *dirname_out to NULL.
246 */
247 static krb5_error_code
get_context_default_dir(krb5_context context,char ** dirname_out)248 get_context_default_dir(krb5_context context, char **dirname_out)
249 {
250 const char *defname;
251 char *dirname;
252
253 *dirname_out = NULL;
254 defname = krb5_cc_default_name(context);
255 if (defname == NULL)
256 return 0;
257 if (strncmp(defname, "DIR:", 4) != 0 ||
258 defname[4] == ':' || defname[4] == '\0')
259 return 0;
260 dirname = strdup(defname + 4);
261 if (dirname == NULL)
262 return ENOMEM;
263 *dirname_out = dirname;
264 return 0;
265 }
266
267 /*
268 * If the default ccache name for context is a subsidiary file in a directory
269 * collection, set *subsidiary_out to the residual value. Otherwise set
270 * *subsidiary_out to NULL.
271 */
272 static krb5_error_code
get_context_subsidiary_file(krb5_context context,char ** subsidiary_out)273 get_context_subsidiary_file(krb5_context context, char **subsidiary_out)
274 {
275 const char *defname;
276 char *residual;
277
278 *subsidiary_out = NULL;
279 defname = krb5_cc_default_name(context);
280 if (defname == NULL || strncmp(defname, "DIR::", 5) != 0)
281 return 0;
282 residual = strdup(defname + 4);
283 if (residual == NULL)
284 return ENOMEM;
285 *subsidiary_out = residual;
286 return 0;
287 }
288
289 static const char * KRB5_CALLCONV
dcc_get_name(krb5_context context,krb5_ccache cache)290 dcc_get_name(krb5_context context, krb5_ccache cache)
291 {
292 dcc_data *data = cache->data;
293
294 return data->residual;
295 }
296
297 /* Construct a cache object given a residual string and file ccache. Take
298 * ownership of fcc on success. */
299 static krb5_error_code
make_cache(const char * residual,krb5_ccache fcc,krb5_ccache * cache_out)300 make_cache(const char *residual, krb5_ccache fcc, krb5_ccache *cache_out)
301 {
302 krb5_ccache cache = NULL;
303 dcc_data *data = NULL;
304 char *residual_copy = NULL;
305
306 cache = malloc(sizeof(*cache));
307 if (cache == NULL)
308 goto oom;
309 data = malloc(sizeof(*data));
310 if (data == NULL)
311 goto oom;
312 residual_copy = strdup(residual);
313 if (residual_copy == NULL)
314 goto oom;
315
316 data->residual = residual_copy;
317 data->fcc = fcc;
318 cache->ops = &krb5_dcc_ops;
319 cache->data = data;
320 cache->magic = KV5M_CCACHE;
321 *cache_out = cache;
322 return 0;
323
324 oom:
325 free(cache);
326 free(data);
327 free(residual_copy);
328 return ENOMEM;
329 }
330
331 static krb5_error_code KRB5_CALLCONV
dcc_resolve(krb5_context context,krb5_ccache * cache_out,const char * residual)332 dcc_resolve(krb5_context context, krb5_ccache *cache_out, const char *residual)
333 {
334 krb5_error_code ret;
335 krb5_ccache fcc;
336 char *primary_path = NULL, *sresidual = NULL, *dirname, *filename;
337
338 *cache_out = NULL;
339
340 if (*residual == ':') {
341 /* This is a subsidiary cache within the directory. */
342 ret = split_path(context, residual + 1, &dirname, &filename);
343 if (ret)
344 return ret;
345
346 ret = verify_dir(context, dirname);
347 free(dirname);
348 free(filename);
349 if (ret)
350 return ret;
351 } else {
352 /* This is the directory itself; resolve to the primary cache. */
353 ret = verify_dir(context, residual);
354 if (ret)
355 return ret;
356
357 ret = primary_pathname(residual, &primary_path);
358 if (ret)
359 goto cleanup;
360
361 ret = read_primary_file(context, primary_path, residual, &sresidual);
362 if (ret == ENOENT) {
363 /* Create an initial primary file. */
364 ret = write_primary_file(primary_path, "tkt");
365 if (ret)
366 goto cleanup;
367 ret = subsidiary_residual(residual, "tkt", &sresidual);
368 }
369 if (ret)
370 goto cleanup;
371 residual = sresidual;
372 }
373
374 ret = krb5_fcc_ops.resolve(context, &fcc, residual + 1);
375 if (ret)
376 goto cleanup;
377 ret = make_cache(residual, fcc, cache_out);
378 if (ret)
379 krb5_fcc_ops.close(context, fcc);
380
381 cleanup:
382 free(primary_path);
383 free(sresidual);
384 return ret;
385 }
386
387 static krb5_error_code KRB5_CALLCONV
dcc_gen_new(krb5_context context,krb5_ccache * cache_out)388 dcc_gen_new(krb5_context context, krb5_ccache *cache_out)
389 {
390 krb5_error_code ret;
391 char *dirname = NULL, *template = NULL, *residual = NULL;
392 krb5_ccache fcc = NULL;
393
394 *cache_out = NULL;
395 ret = get_context_default_dir(context, &dirname);
396 if (ret)
397 return ret;
398 if (dirname == NULL) {
399 k5_setmsg(context, KRB5_DCC_CANNOT_CREATE,
400 _("Can't create new subsidiary cache because default cache "
401 "is not a directory collection"));
402 return KRB5_DCC_CANNOT_CREATE;
403 }
404 ret = verify_dir(context, dirname);
405 if (ret)
406 goto cleanup;
407 ret = k5_path_join(dirname, "tktXXXXXX", &template);
408 if (ret)
409 goto cleanup;
410 ret = krb5int_fcc_new_unique(context, template, &fcc);
411 if (ret)
412 goto cleanup;
413 if (asprintf(&residual, ":%s", template) < 0) {
414 ret = ENOMEM;
415 goto cleanup;
416 }
417 ret = make_cache(residual, fcc, cache_out);
418 if (ret)
419 goto cleanup;
420 fcc = NULL;
421
422 cleanup:
423 if (fcc != NULL)
424 krb5_fcc_ops.destroy(context, fcc);
425 free(dirname);
426 free(template);
427 free(residual);
428 return ret;
429 }
430
431 static krb5_error_code KRB5_CALLCONV
dcc_init(krb5_context context,krb5_ccache cache,krb5_principal princ)432 dcc_init(krb5_context context, krb5_ccache cache, krb5_principal princ)
433 {
434 dcc_data *data = cache->data;
435
436 return krb5_fcc_ops.init(context, data->fcc, princ);
437 }
438
439 static krb5_error_code KRB5_CALLCONV
dcc_destroy(krb5_context context,krb5_ccache cache)440 dcc_destroy(krb5_context context, krb5_ccache cache)
441 {
442 dcc_data *data = cache->data;
443 krb5_error_code ret;
444
445 ret = krb5_fcc_ops.destroy(context, data->fcc);
446 free(data->residual);
447 free(data);
448 free(cache);
449 return ret;
450 }
451
452 static krb5_error_code KRB5_CALLCONV
dcc_close(krb5_context context,krb5_ccache cache)453 dcc_close(krb5_context context, krb5_ccache cache)
454 {
455 dcc_data *data = cache->data;
456 krb5_error_code ret;
457
458 ret = krb5_fcc_ops.close(context, data->fcc);
459 free(data->residual);
460 free(data);
461 free(cache);
462 return ret;
463 }
464
465 static krb5_error_code KRB5_CALLCONV
dcc_store(krb5_context context,krb5_ccache cache,krb5_creds * creds)466 dcc_store(krb5_context context, krb5_ccache cache, krb5_creds *creds)
467 {
468 dcc_data *data = cache->data;
469
470 return krb5_fcc_ops.store(context, data->fcc, creds);
471 }
472
473 static krb5_error_code KRB5_CALLCONV
dcc_retrieve(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * mcreds,krb5_creds * creds)474 dcc_retrieve(krb5_context context, krb5_ccache cache, krb5_flags flags,
475 krb5_creds *mcreds, krb5_creds *creds)
476 {
477 dcc_data *data = cache->data;
478
479 return krb5_fcc_ops.retrieve(context, data->fcc, flags, mcreds,
480 creds);
481 }
482
483 static krb5_error_code KRB5_CALLCONV
dcc_get_princ(krb5_context context,krb5_ccache cache,krb5_principal * princ_out)484 dcc_get_princ(krb5_context context, krb5_ccache cache,
485 krb5_principal *princ_out)
486 {
487 dcc_data *data = cache->data;
488
489 return krb5_fcc_ops.get_princ(context, data->fcc, princ_out);
490 }
491
492 static krb5_error_code KRB5_CALLCONV
dcc_get_first(krb5_context context,krb5_ccache cache,krb5_cc_cursor * cursor)493 dcc_get_first(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor)
494 {
495 dcc_data *data = cache->data;
496
497 return krb5_fcc_ops.get_first(context, data->fcc, cursor);
498 }
499
500 static krb5_error_code KRB5_CALLCONV
dcc_get_next(krb5_context context,krb5_ccache cache,krb5_cc_cursor * cursor,krb5_creds * creds)501 dcc_get_next(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
502 krb5_creds *creds)
503 {
504 dcc_data *data = cache->data;
505
506 return krb5_fcc_ops.get_next(context, data->fcc, cursor, creds);
507 }
508
509 static krb5_error_code KRB5_CALLCONV
dcc_end_get(krb5_context context,krb5_ccache cache,krb5_cc_cursor * cursor)510 dcc_end_get(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor)
511 {
512 dcc_data *data = cache->data;
513
514 return krb5_fcc_ops.end_get(context, data->fcc, cursor);
515 }
516
517 static krb5_error_code KRB5_CALLCONV
dcc_remove_cred(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * creds)518 dcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
519 krb5_creds *creds)
520 {
521 dcc_data *data = cache->data;
522
523 return krb5_fcc_ops.remove_cred(context, data->fcc, flags, creds);
524 }
525
526 static krb5_error_code KRB5_CALLCONV
dcc_set_flags(krb5_context context,krb5_ccache cache,krb5_flags flags)527 dcc_set_flags(krb5_context context, krb5_ccache cache, krb5_flags flags)
528 {
529 dcc_data *data = cache->data;
530
531 return krb5_fcc_ops.set_flags(context, data->fcc, flags);
532 }
533
534 static krb5_error_code KRB5_CALLCONV
dcc_get_flags(krb5_context context,krb5_ccache cache,krb5_flags * flags_out)535 dcc_get_flags(krb5_context context, krb5_ccache cache, krb5_flags *flags_out)
536 {
537 dcc_data *data = cache->data;
538
539 return krb5_fcc_ops.get_flags(context, data->fcc, flags_out);
540 }
541
542 struct dcc_ptcursor_data {
543 char *primary;
544 char *dirname;
545 DIR *dir;
546 krb5_boolean first;
547 };
548
549 /* Construct a cursor, taking ownership of dirname, primary, and dir on
550 * success. */
551 static krb5_error_code
make_cursor(char * dirname,char * primary,DIR * dir,krb5_cc_ptcursor * cursor_out)552 make_cursor(char *dirname, char *primary, DIR *dir,
553 krb5_cc_ptcursor *cursor_out)
554 {
555 krb5_cc_ptcursor cursor;
556 struct dcc_ptcursor_data *data;
557
558 *cursor_out = NULL;
559
560 data = malloc(sizeof(*data));
561 if (data == NULL)
562 return ENOMEM;
563 cursor = malloc(sizeof(*cursor));
564 if (cursor == NULL) {
565 free(data);
566 return ENOMEM;
567 }
568
569 data->dirname = dirname;
570 data->primary = primary;
571 data->dir = dir;
572 data->first = TRUE;
573 cursor->ops = &krb5_dcc_ops;
574 cursor->data = data;
575 *cursor_out = cursor;
576 return 0;
577 }
578
579 static krb5_error_code KRB5_CALLCONV
dcc_ptcursor_new(krb5_context context,krb5_cc_ptcursor * cursor_out)580 dcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor_out)
581 {
582 krb5_error_code ret;
583 char *dirname = NULL, *primary_path = NULL, *primary = NULL;
584 DIR *dir = NULL;
585
586 *cursor_out = NULL;
587
588 /* If the default cache is a subsidiary file, make a cursor with the
589 * specified file as the primary but with no directory collection. */
590 ret = get_context_subsidiary_file(context, &primary);
591 if (ret)
592 goto cleanup;
593 if (primary != NULL) {
594 ret = make_cursor(NULL, primary, NULL, cursor_out);
595 if (ret)
596 free(primary);
597 return ret;
598 }
599
600 /* Open the directory for the context's default cache. */
601 ret = get_context_default_dir(context, &dirname);
602 if (ret || dirname == NULL)
603 goto cleanup;
604 dir = opendir(dirname);
605 if (dir == NULL)
606 goto cleanup;
607
608 /* Fetch the primary cache name if possible. */
609 ret = primary_pathname(dirname, &primary_path);
610 if (ret)
611 goto cleanup;
612 ret = read_primary_file(context, primary_path, dirname, &primary);
613 if (ret)
614 krb5_clear_error_message(context);
615
616 ret = make_cursor(dirname, primary, dir, cursor_out);
617 if (ret)
618 goto cleanup;
619 dirname = primary = NULL;
620 dir = NULL;
621
622 cleanup:
623 free(dirname);
624 free(primary_path);
625 free(primary);
626 if (dir)
627 closedir(dir);
628 /* Return an empty cursor if we fail for any reason. */
629 if (*cursor_out == NULL)
630 return make_cursor(NULL, NULL, NULL, cursor_out);
631 return 0;
632 }
633
634 static krb5_error_code KRB5_CALLCONV
dcc_ptcursor_next(krb5_context context,krb5_cc_ptcursor cursor,krb5_ccache * cache_out)635 dcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor,
636 krb5_ccache *cache_out)
637 {
638 struct dcc_ptcursor_data *data = cursor->data;
639 struct dirent *ent;
640 char *residual;
641 krb5_error_code ret;
642 struct stat sb;
643
644 *cache_out = NULL;
645
646 /* Return the primary or specified subsidiary cache if we haven't yet. */
647 if (data->first) {
648 data->first = FALSE;
649 if (data->primary != NULL && stat(data->primary + 1, &sb) == 0)
650 return dcc_resolve(context, cache_out, data->primary);
651 }
652
653 if (data->dir == NULL) /* No directory collection */
654 return 0;
655
656 /* Look for the next filename of the correct form, without repeating the
657 * primary cache. */
658 while ((ent = readdir(data->dir)) != NULL) {
659 if (!filename_is_cache(ent->d_name))
660 continue;
661 ret = subsidiary_residual(data->dirname, ent->d_name, &residual);
662 if (ret)
663 return ret;
664 if (data->primary != NULL && strcmp(residual, data->primary) == 0) {
665 free(residual);
666 continue;
667 }
668 ret = dcc_resolve(context, cache_out, residual);
669 free(residual);
670 return ret;
671 }
672
673 /* We exhausted the directory without finding a cache to yield. */
674 closedir(data->dir);
675 data->dir = NULL;
676 return 0;
677 }
678
679 static krb5_error_code KRB5_CALLCONV
dcc_ptcursor_free(krb5_context context,krb5_cc_ptcursor * cursor)680 dcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
681 {
682 struct dcc_ptcursor_data *data = (*cursor)->data;
683
684 if (data->dir)
685 closedir(data->dir);
686 free(data->dirname);
687 free(data->primary);
688 free(data);
689 free(*cursor);
690 *cursor = NULL;
691 return 0;
692 }
693
694 static krb5_error_code KRB5_CALLCONV
dcc_replace(krb5_context context,krb5_ccache cache,krb5_principal princ,krb5_creds ** creds)695 dcc_replace(krb5_context context, krb5_ccache cache, krb5_principal princ,
696 krb5_creds **creds)
697 {
698 dcc_data *data = cache->data;
699
700 return krb5_fcc_ops.replace(context, data->fcc, princ, creds);
701 }
702
703 static krb5_error_code KRB5_CALLCONV
dcc_lock(krb5_context context,krb5_ccache cache)704 dcc_lock(krb5_context context, krb5_ccache cache)
705 {
706 dcc_data *data = cache->data;
707
708 return krb5_fcc_ops.lock(context, data->fcc);
709 }
710
711 static krb5_error_code KRB5_CALLCONV
dcc_unlock(krb5_context context,krb5_ccache cache)712 dcc_unlock(krb5_context context, krb5_ccache cache)
713 {
714 dcc_data *data = cache->data;
715
716 return krb5_fcc_ops.unlock(context, data->fcc);
717 }
718
719 static krb5_error_code KRB5_CALLCONV
dcc_switch_to(krb5_context context,krb5_ccache cache)720 dcc_switch_to(krb5_context context, krb5_ccache cache)
721 {
722 dcc_data *data = cache->data;
723 char *primary_path = NULL, *dirname = NULL, *filename = NULL;
724 krb5_error_code ret;
725
726 ret = split_path(context, data->residual + 1, &dirname, &filename);
727 if (ret)
728 return ret;
729
730 ret = primary_pathname(dirname, &primary_path);
731 if (ret)
732 goto cleanup;
733
734 ret = write_primary_file(primary_path, filename);
735
736 cleanup:
737 free(primary_path);
738 free(dirname);
739 free(filename);
740 return ret;
741 }
742
743 const krb5_cc_ops krb5_dcc_ops = {
744 0,
745 "DIR",
746 dcc_get_name,
747 dcc_resolve,
748 dcc_gen_new,
749 dcc_init,
750 dcc_destroy,
751 dcc_close,
752 dcc_store,
753 dcc_retrieve,
754 dcc_get_princ,
755 dcc_get_first,
756 dcc_get_next,
757 dcc_end_get,
758 dcc_remove_cred,
759 dcc_set_flags,
760 dcc_get_flags,
761 dcc_ptcursor_new,
762 dcc_ptcursor_next,
763 dcc_ptcursor_free,
764 dcc_replace,
765 NULL, /* wasdefault */
766 dcc_lock,
767 dcc_unlock,
768 dcc_switch_to,
769 };
770
771 #endif /* not _WIN32 */
772