Properly terminate ifdef conditional in krb5-types.h
[heimdal.git] / lib / krb5 / dcache.c
blob61a97a89d5cfdedec18adb23b1447aa5eded7c8d
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 typedef struct krb5_dcache{
39 krb5_ccache fcache;
40 char *dir;
41 char *name;
42 } krb5_dcache;
44 #define DCACHE(X) ((krb5_dcache*)(X)->data.data)
45 #define D2FCACHE(X) ((X)->fcache)
47 static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache);
48 static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **);
51 static char *
52 primary_create(krb5_dcache *dc)
54 char *primary = NULL;
56 asprintf(&primary, "%s/primary", dc->dir);
57 if (primary == NULL)
58 return NULL;
60 return primary;
63 static int
64 is_filename_cacheish(const char *name)
66 return strncmp(name, "tkt", 3) == 0;
70 static krb5_error_code
71 set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual)
73 char *path = NULL, *primary = NULL;
74 krb5_error_code ret;
75 struct iovec iov[2];
76 size_t len;
77 int fd = -1;
79 if (!is_filename_cacheish(residual)) {
80 krb5_set_error_message(context, KRB5_CC_FORMAT,
81 "name %s is not a cache (doesn't start with tkt)", residual);
82 return KRB5_CC_FORMAT;
85 asprintf(&path, "%s/primary-XXXXXX", dc->dir);
86 if (path == NULL)
87 return krb5_enomem(context);
89 fd = mkstemp(path);
90 if (fd < 0) {
91 ret = errno;
92 goto out;
94 rk_cloexec(fd);
95 #ifndef _WIN32
96 if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
97 ret = errno;
98 goto out;
100 #endif
101 len = strlen(residual);
103 iov[0].iov_base = rk_UNCONST(residual);
104 iov[0].iov_len = len;
105 iov[1].iov_base = "\n";
106 iov[1].iov_len = 1;
108 if (writev(fd, iov, sizeof(iov)/sizeof(iov[0])) != len + 1) {
109 ret = errno;
110 goto out;
113 primary = primary_create(dc);
114 if (primary == NULL) {
115 ret = krb5_enomem(context);
116 goto out;
119 if (rename(path, primary) < 0) {
120 ret = errno;
121 goto out;
124 close(fd);
125 fd = -1;
127 ret = 0;
128 out:
129 if (fd >= 0) {
130 (void)unlink(path);
131 close(fd);
133 if (path)
134 free(path);
135 if (primary)
136 free(primary);
138 return ret;
141 static krb5_error_code
142 get_default_cache(krb5_context context, krb5_dcache *dc, char **residual)
144 krb5_error_code ret;
145 char buf[MAXPATHLEN];
146 char *primary;
147 FILE *f;
149 primary = primary_create(dc);
150 if (primary == NULL)
151 return krb5_enomem(context);
153 f = fopen(primary, "r");
154 if (f == NULL) {
155 if (errno == ENOENT) {
156 free(primary);
157 *residual = strdup("tkt");
158 if (*residual == NULL)
159 return krb5_enomem(context);
160 return 0;
162 ret = errno;
163 krb5_set_error_message(context, ret, "failed to open %s", primary);
164 free(primary);
165 return ret;
168 if (fgets(buf, sizeof(buf), f) == NULL) {
169 ret = ferror(f);
170 fclose(f);
171 krb5_set_error_message(context, ret, "read file %s", primary);
172 free(primary);
173 return ret;
175 fclose(f);
177 buf[strcspn(buf, "\r\n")] = '\0';
179 if (!is_filename_cacheish(buf)) {
180 krb5_set_error_message(context, KRB5_CC_FORMAT,
181 "name in %s is not a cache (doesn't start with tkt)", primary);
182 free(primary);
183 return KRB5_CC_FORMAT;
186 free(primary);
188 *residual = strdup(buf);
189 if (*residual == NULL)
190 return krb5_enomem(context);
192 return 0;
197 static const char* KRB5_CALLCONV
198 dcc_get_name(krb5_context context,
199 krb5_ccache id)
201 krb5_dcache *dc = DCACHE(id);
202 return dc->name;
206 static krb5_error_code
207 verify_directory(krb5_context context, const char *path)
209 struct stat sb;
211 if (stat(path, &sb) != 0) {
212 if (errno == ENOENT) {
213 /* XXX should use mkdirx_np() */
214 if (rk_mkdir(path, S_IRWXU) == 0)
215 return 0;
217 krb5_set_error_message(context, ENOENT,
218 N_("DIR directory %s doesn't exists", ""), path);
219 return ENOENT;
220 } else {
221 int ret = errno;
222 krb5_set_error_message(context, ret,
223 N_("DIR directory %s is bad: %s", ""), path, strerror(ret));
224 return errno;
227 if (!S_ISDIR(sb.st_mode)) {
228 krb5_set_error_message(context, KRB5_CC_BADNAME,
229 N_("DIR directory %s is not a directory", ""), path);
230 return KRB5_CC_BADNAME;
233 return 0;
236 static void
237 dcc_release(krb5_context context, krb5_dcache *dc)
239 if (dc->fcache)
240 krb5_cc_close(context, dc->fcache);
241 if (dc->dir)
242 free(dc->dir);
243 if (dc->name)
244 free(dc->name);
245 memset(dc, 0, sizeof(*dc));
246 free(dc);
249 static krb5_error_code KRB5_CALLCONV
250 dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
252 char *filename = NULL;
253 krb5_error_code ret;
254 krb5_dcache *dc;
255 const char *p;
257 p = res;
258 do {
259 p = strstr(p, "..");
260 if (p && (p == res || ISPATHSEP(p[-1])) && (ISPATHSEP(p[2]) || p[2] == '\0')) {
261 krb5_set_error_message(context, KRB5_CC_FORMAT,
262 N_("Path contains a .. component", ""));
263 return KRB5_CC_FORMAT;
265 if (p)
266 p += 3;
267 } while (p);
269 dc = calloc(1, sizeof(*dc));
270 if (dc == NULL) {
271 krb5_set_error_message(context, KRB5_CC_NOMEM,
272 N_("malloc: out of memory", ""));
273 return KRB5_CC_NOMEM;
276 /* check for explicit component */
277 if (res[0] == ':') {
278 char *q;
280 dc->dir = strdup(&res[1]);
281 #ifdef _WIN32
282 q = strrchr(dc->dir, '\\');
283 if (q == NULL)
284 #endif
285 q = strrchr(dc->dir, '/');
286 if (q) {
287 *q++ = '\0';
288 } else {
289 krb5_set_error_message(context, KRB5_CC_FORMAT, N_("Cache not an absolute path: %s", ""), dc->dir);
290 dcc_release(context, dc);
291 return KRB5_CC_FORMAT;
294 if (!is_filename_cacheish(q)) {
295 krb5_set_error_message(context, KRB5_CC_FORMAT,
296 N_("Name %s is not a cache (doesn't start with tkt)", ""), q);
297 dcc_release(context, dc);
298 return KRB5_CC_FORMAT;
301 ret = verify_directory(context, dc->dir);
302 if (ret) {
303 dcc_release(context, dc);
304 return ret;
307 dc->name = strdup(res);
308 if (dc->name == NULL) {
309 dcc_release(context, dc);
310 return krb5_enomem(context);
313 } else {
314 char *residual;
315 size_t len;
317 dc->dir = strdup(res);
318 if (dc->dir == NULL) {
319 dcc_release(context, dc);
320 return krb5_enomem(context);
323 len = strlen(dc->dir);
325 if (ISPATHSEP(dc->dir[len - 1]))
326 dc->dir[len - 1] = '\0';
328 ret = verify_directory(context, dc->dir);
329 if (ret) {
330 dcc_release(context, dc);
331 return ret;
334 ret = get_default_cache(context, dc, &residual);
335 if (ret) {
336 dcc_release(context, dc);
337 return ret;
339 asprintf(&dc->name, ":%s/%s", dc->dir, residual);
340 free(residual);
341 if (dc->name == NULL) {
342 dcc_release(context, dc);
343 return krb5_enomem(context);
347 asprintf(&filename, "FILE%s", dc->name);
348 if (filename == NULL) {
349 dcc_release(context, dc);
350 return krb5_enomem(context);
353 ret = krb5_cc_resolve(context, filename, &dc->fcache);
354 free(filename);
355 if (ret) {
356 dcc_release(context, dc);
357 return ret;
361 (*id)->data.data = dc;
362 (*id)->data.length = sizeof(*dc);
363 return 0;
366 static char *
367 copy_default_dcc_cache(krb5_context context)
369 const char *defname;
370 krb5_error_code ret;
371 char *name = NULL;
372 size_t len;
374 len = strlen(krb5_dcc_ops.prefix);
376 defname = krb5_cc_default_name(context);
377 if (defname == NULL ||
378 strncmp(defname, krb5_dcc_ops.prefix, len) != 0 ||
379 defname[len] != ':')
381 ret = dcc_get_default_name(context, &name);
382 if (ret)
383 return NULL;
385 return name;
386 } else {
387 return strdup(&defname[len + 1]);
392 static krb5_error_code KRB5_CALLCONV
393 dcc_gen_new(krb5_context context, krb5_ccache *id)
395 krb5_error_code ret;
396 char *name = NULL;
397 krb5_dcache *dc;
398 int fd;
399 size_t len;
401 name = copy_default_dcc_cache(context);
402 if (name == NULL) {
403 krb5_set_error_message(context, KRB5_CC_FORMAT,
404 N_("Can't generate DIR caches unless its the default type", ""));
405 return KRB5_CC_FORMAT;
408 len = strlen(krb5_dcc_ops.prefix);
409 if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':')
410 ++len;
411 else
412 len = 0;
414 ret = dcc_resolve(context, id, name + len);
415 free(name);
416 name = NULL;
417 if (ret)
418 return ret;
420 dc = DCACHE((*id));
422 asprintf(&name, ":%s/tktXXXXXX", dc->dir);
423 if (name == NULL) {
424 dcc_close(context, *id);
425 return krb5_enomem(context);
427 free(dc->name);
429 fd = mkstemp(&name[1]);
430 if (fd < 0) {
431 dcc_close(context, *id);
432 return krb5_enomem(context);
434 close(fd);
436 dc->name = name;
438 return 0;
441 static krb5_error_code KRB5_CALLCONV
442 dcc_initialize(krb5_context context,
443 krb5_ccache id,
444 krb5_principal primary_principal)
446 krb5_dcache *dc = DCACHE(id);
447 return krb5_cc_initialize(context, D2FCACHE(dc), primary_principal);
450 static krb5_error_code KRB5_CALLCONV
451 dcc_close(krb5_context context,
452 krb5_ccache id)
454 dcc_release(context, DCACHE(id));
455 return 0;
458 static krb5_error_code KRB5_CALLCONV
459 dcc_destroy(krb5_context context,
460 krb5_ccache id)
462 krb5_dcache *dc = DCACHE(id);
463 krb5_ccache fcache = D2FCACHE(dc);
464 dc->fcache = NULL;
465 return krb5_cc_destroy(context, fcache);
468 static krb5_error_code KRB5_CALLCONV
469 dcc_store_cred(krb5_context context,
470 krb5_ccache id,
471 krb5_creds *creds)
473 krb5_dcache *dc = DCACHE(id);
474 return krb5_cc_store_cred(context, D2FCACHE(dc), creds);
477 static krb5_error_code KRB5_CALLCONV
478 dcc_get_principal(krb5_context context,
479 krb5_ccache id,
480 krb5_principal *principal)
482 krb5_dcache *dc = DCACHE(id);
483 return krb5_cc_get_principal(context, D2FCACHE(dc), principal);
486 static krb5_error_code KRB5_CALLCONV
487 dcc_get_first (krb5_context context,
488 krb5_ccache id,
489 krb5_cc_cursor *cursor)
491 krb5_dcache *dc = DCACHE(id);
492 return krb5_cc_start_seq_get(context, D2FCACHE(dc), cursor);
495 static krb5_error_code KRB5_CALLCONV
496 dcc_get_next (krb5_context context,
497 krb5_ccache id,
498 krb5_cc_cursor *cursor,
499 krb5_creds *creds)
501 krb5_dcache *dc = DCACHE(id);
502 return krb5_cc_next_cred(context, D2FCACHE(dc), cursor, creds);
505 static krb5_error_code KRB5_CALLCONV
506 dcc_end_get (krb5_context context,
507 krb5_ccache id,
508 krb5_cc_cursor *cursor)
510 krb5_dcache *dc = DCACHE(id);
511 return krb5_cc_end_seq_get(context, D2FCACHE(dc), cursor);
514 static krb5_error_code KRB5_CALLCONV
515 dcc_remove_cred(krb5_context context,
516 krb5_ccache id,
517 krb5_flags which,
518 krb5_creds *cred)
520 krb5_dcache *dc = DCACHE(id);
521 return krb5_cc_remove_cred(context, D2FCACHE(dc), which, cred);
524 static krb5_error_code KRB5_CALLCONV
525 dcc_set_flags(krb5_context context,
526 krb5_ccache id,
527 krb5_flags flags)
529 krb5_dcache *dc = DCACHE(id);
530 return krb5_cc_set_flags(context, D2FCACHE(dc), flags);
533 static int KRB5_CALLCONV
534 dcc_get_version(krb5_context context,
535 krb5_ccache id)
537 krb5_dcache *dc = DCACHE(id);
538 return krb5_cc_get_version(context, D2FCACHE(dc));
541 struct dcache_iter {
542 int first;
543 krb5_dcache *dc;
546 static krb5_error_code KRB5_CALLCONV
547 dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
549 struct dcache_iter *iter;
550 char *name;
552 iter = calloc(1, sizeof(*iter));
553 if (iter == NULL)
554 return krb5_enomem(context);
555 iter->first = 1;
557 name = copy_default_dcc_cache(context);
558 if (name == NULL) {
559 krb5_set_error_message(context, KRB5_CC_FORMAT,
560 N_("Can't generate DIR caches unless its the default type", ""));
561 return KRB5_CC_FORMAT;
564 #if 1
565 free(name);
566 return KRB5_CC_FORMAT;
567 #else
569 /* XXX */
570 ret = dcc_resolve(context, NULL, name);
571 free(name);
573 *cursor = iter;
574 return 0;
575 #endif
578 static krb5_error_code KRB5_CALLCONV
579 dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
581 struct dcache_iter *iter = cursor;
583 if (iter == NULL)
584 return krb5_einval(context, 2);
586 if (!iter->first) {
587 krb5_clear_error_message(context);
588 return KRB5_CC_END;
590 iter->first = 0;
592 return KRB5_CC_END;
595 static krb5_error_code KRB5_CALLCONV
596 dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
598 struct dcache_iter *iter = cursor;
600 if (iter == NULL)
601 return krb5_einval(context, 2);
603 if (iter->dc)
604 dcc_release(context, iter->dc);
605 free(iter);
606 return 0;
609 static krb5_error_code KRB5_CALLCONV
610 dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
612 krb5_dcache *dcfrom = DCACHE(from);
613 krb5_dcache *dcto = DCACHE(to);
614 return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
617 static krb5_error_code KRB5_CALLCONV
618 dcc_get_default_name(krb5_context context, char **str)
620 return _krb5_expand_default_cc_name(context,
621 KRB5_DEFAULT_CCNAME_DIR,
622 str);
625 static krb5_error_code KRB5_CALLCONV
626 dcc_set_default(krb5_context context, krb5_ccache id)
628 krb5_dcache *dc = DCACHE(id);
629 const char *name;
631 name = krb5_cc_get_name(context, D2FCACHE(dc));
632 if (name == NULL)
633 return ENOENT;
635 return set_default_cache(context, dc, name);
638 static krb5_error_code KRB5_CALLCONV
639 dcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
641 krb5_dcache *dc = DCACHE(id);
642 return krb5_cc_last_change_time(context, D2FCACHE(dc), mtime);
645 static krb5_error_code KRB5_CALLCONV
646 dcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
648 krb5_dcache *dc = DCACHE(id);
649 return krb5_cc_set_kdc_offset(context, D2FCACHE(dc), kdc_offset);
652 static krb5_error_code KRB5_CALLCONV
653 dcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
655 krb5_dcache *dc = DCACHE(id);
656 return krb5_cc_get_kdc_offset(context, D2FCACHE(dc), kdc_offset);
661 * Variable containing the DIR based credential cache implemention.
663 * @ingroup krb5_ccache
666 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops = {
667 KRB5_CC_OPS_VERSION,
668 "DIR",
669 dcc_get_name,
670 dcc_resolve,
671 dcc_gen_new,
672 dcc_initialize,
673 dcc_destroy,
674 dcc_close,
675 dcc_store_cred,
676 NULL, /* dcc_retrieve */
677 dcc_get_principal,
678 dcc_get_first,
679 dcc_get_next,
680 dcc_end_get,
681 dcc_remove_cred,
682 dcc_set_flags,
683 dcc_get_version,
684 dcc_get_cache_first,
685 dcc_get_cache_next,
686 dcc_end_cache_get,
687 dcc_move,
688 dcc_get_default_name,
689 dcc_set_default,
690 dcc_lastchange,
691 dcc_set_kdc_offset,
692 dcc_get_kdc_offset