Move external libdeps after our own
[heimdal.git] / lib / krb5 / dcache.c
blobc89e157de5a35a646f02f2a7c0a492620f2ba412
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 *residual = NULL;
150 primary = primary_create(dc);
151 if (primary == NULL)
152 return krb5_enomem(context);
154 f = fopen(primary, "r");
155 if (f == NULL) {
156 if (errno == ENOENT) {
157 free(primary);
158 *residual = strdup("tkt");
159 if (*residual == NULL)
160 return krb5_enomem(context);
161 return 0;
163 ret = errno;
164 krb5_set_error_message(context, ret, "failed to open %s", primary);
165 free(primary);
166 return ret;
169 if (fgets(buf, sizeof(buf), f) == NULL) {
170 ret = ferror(f);
171 fclose(f);
172 krb5_set_error_message(context, ret, "read file %s", primary);
173 free(primary);
174 return ret;
176 fclose(f);
178 buf[strcspn(buf, "\r\n")] = '\0';
180 if (!is_filename_cacheish(buf)) {
181 krb5_set_error_message(context, KRB5_CC_FORMAT,
182 "name in %s is not a cache (doesn't start with tkt)", primary);
183 free(primary);
184 return KRB5_CC_FORMAT;
187 free(primary);
189 *residual = strdup(buf);
190 if (*residual == NULL)
191 return krb5_enomem(context);
193 return 0;
198 static const char* KRB5_CALLCONV
199 dcc_get_name(krb5_context context,
200 krb5_ccache id)
202 krb5_dcache *dc = DCACHE(id);
203 return dc->name;
207 static krb5_error_code
208 verify_directory(krb5_context context, const char *path)
210 struct stat sb;
212 if (stat(path, &sb) != 0) {
213 if (errno == ENOENT) {
214 /* XXX should use mkdirx_np() */
215 if (rk_mkdir(path, S_IRWXU) == 0)
216 return 0;
218 krb5_set_error_message(context, ENOENT,
219 N_("DIR directory %s doesn't exists", ""), path);
220 return ENOENT;
221 } else {
222 int ret = errno;
223 krb5_set_error_message(context, ret,
224 N_("DIR directory %s is bad: %s", ""), path, strerror(ret));
225 return errno;
228 if (!S_ISDIR(sb.st_mode)) {
229 krb5_set_error_message(context, KRB5_CC_BADNAME,
230 N_("DIR directory %s is not a directory", ""), path);
231 return KRB5_CC_BADNAME;
234 return 0;
237 static void
238 dcc_release(krb5_context context, krb5_dcache *dc)
240 if (dc->fcache)
241 krb5_cc_close(context, dc->fcache);
242 if (dc->dir)
243 free(dc->dir);
244 if (dc->name)
245 free(dc->name);
246 memset(dc, 0, sizeof(*dc));
247 free(dc);
250 static krb5_error_code KRB5_CALLCONV
251 dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
253 char *filename = NULL;
254 krb5_error_code ret;
255 krb5_dcache *dc;
256 const char *p;
258 p = res;
259 do {
260 p = strstr(p, "..");
261 if (p && (p == res || ISPATHSEP(p[-1])) && (ISPATHSEP(p[2]) || p[2] == '\0')) {
262 krb5_set_error_message(context, KRB5_CC_FORMAT,
263 N_("Path contains a .. component", ""));
264 return KRB5_CC_FORMAT;
266 if (p)
267 p += 3;
268 } while (p);
270 dc = calloc(1, sizeof(*dc));
271 if (dc == NULL) {
272 krb5_set_error_message(context, KRB5_CC_NOMEM,
273 N_("malloc: out of memory", ""));
274 return KRB5_CC_NOMEM;
277 /* check for explicit component */
278 if (res[0] == ':') {
279 char *q;
281 dc->dir = strdup(&res[1]);
282 #ifdef _WIN32
283 q = strrchr(dc->dir, '\\');
284 if (q == NULL)
285 #endif
286 q = strrchr(dc->dir, '/');
287 if (q) {
288 *q++ = '\0';
289 } else {
290 krb5_set_error_message(context, KRB5_CC_FORMAT, N_("Cache not an absolute path: %s", ""), dc->dir);
291 dcc_release(context, dc);
292 return KRB5_CC_FORMAT;
295 if (!is_filename_cacheish(q)) {
296 krb5_set_error_message(context, KRB5_CC_FORMAT,
297 N_("Name %s is not a cache (doesn't start with tkt)", ""), q);
298 dcc_release(context, dc);
299 return KRB5_CC_FORMAT;
302 ret = verify_directory(context, dc->dir);
303 if (ret) {
304 dcc_release(context, dc);
305 return ret;
308 dc->name = strdup(res);
309 if (dc->name == NULL) {
310 dcc_release(context, dc);
311 return krb5_enomem(context);
314 } else {
315 char *residual;
316 size_t len;
318 dc->dir = strdup(res);
319 if (dc->dir == NULL) {
320 dcc_release(context, dc);
321 return krb5_enomem(context);
324 len = strlen(dc->dir);
326 if (ISPATHSEP(dc->dir[len - 1]))
327 dc->dir[len - 1] = '\0';
329 ret = verify_directory(context, dc->dir);
330 if (ret) {
331 dcc_release(context, dc);
332 return ret;
335 ret = get_default_cache(context, dc, &residual);
336 if (ret) {
337 dcc_release(context, dc);
338 return ret;
340 asprintf(&dc->name, ":%s/%s", dc->dir, residual);
341 free(residual);
342 if (dc->name == NULL) {
343 dcc_release(context, dc);
344 return krb5_enomem(context);
348 asprintf(&filename, "FILE%s", dc->name);
349 if (filename == NULL) {
350 dcc_release(context, dc);
351 return krb5_enomem(context);
354 ret = krb5_cc_resolve(context, filename, &dc->fcache);
355 free(filename);
356 if (ret) {
357 dcc_release(context, dc);
358 return ret;
362 (*id)->data.data = dc;
363 (*id)->data.length = sizeof(*dc);
364 return 0;
367 static char *
368 copy_default_dcc_cache(krb5_context context)
370 const char *defname;
371 krb5_error_code ret;
372 char *name = NULL;
373 size_t len;
375 len = strlen(krb5_dcc_ops.prefix);
377 defname = krb5_cc_default_name(context);
378 if (defname == NULL ||
379 strncmp(defname, krb5_dcc_ops.prefix, len) != 0 ||
380 defname[len] != ':')
382 ret = dcc_get_default_name(context, &name);
383 if (ret)
384 return NULL;
386 return name;
387 } else {
388 return strdup(&defname[len + 1]);
393 static krb5_error_code KRB5_CALLCONV
394 dcc_gen_new(krb5_context context, krb5_ccache *id)
396 krb5_error_code ret;
397 char *name = NULL;
398 krb5_dcache *dc;
399 int fd;
400 size_t len;
402 name = copy_default_dcc_cache(context);
403 if (name == NULL) {
404 krb5_set_error_message(context, KRB5_CC_FORMAT,
405 N_("Can't generate DIR caches unless its the default type", ""));
406 return KRB5_CC_FORMAT;
409 len = strlen(krb5_dcc_ops.prefix);
410 if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':')
411 ++len;
412 else
413 len = 0;
415 ret = dcc_resolve(context, id, name + len);
416 free(name);
417 name = NULL;
418 if (ret)
419 return ret;
421 dc = DCACHE((*id));
423 asprintf(&name, ":%s/tktXXXXXX", dc->dir);
424 if (name == NULL) {
425 dcc_close(context, *id);
426 return krb5_enomem(context);
429 fd = mkstemp(&name[1]);
430 if (fd < 0) {
431 dcc_close(context, *id);
432 return krb5_enomem(context);
434 close(fd);
436 free(dc->name);
437 dc->name = name;
439 return 0;
442 static krb5_error_code KRB5_CALLCONV
443 dcc_initialize(krb5_context context,
444 krb5_ccache id,
445 krb5_principal primary_principal)
447 krb5_dcache *dc = DCACHE(id);
448 return krb5_cc_initialize(context, D2FCACHE(dc), primary_principal);
451 static krb5_error_code KRB5_CALLCONV
452 dcc_close(krb5_context context,
453 krb5_ccache id)
455 dcc_release(context, DCACHE(id));
456 return 0;
459 static krb5_error_code KRB5_CALLCONV
460 dcc_destroy(krb5_context context,
461 krb5_ccache id)
463 krb5_dcache *dc = DCACHE(id);
464 krb5_ccache fcache = D2FCACHE(dc);
465 dc->fcache = NULL;
466 return krb5_cc_destroy(context, fcache);
469 static krb5_error_code KRB5_CALLCONV
470 dcc_store_cred(krb5_context context,
471 krb5_ccache id,
472 krb5_creds *creds)
474 krb5_dcache *dc = DCACHE(id);
475 return krb5_cc_store_cred(context, D2FCACHE(dc), creds);
478 static krb5_error_code KRB5_CALLCONV
479 dcc_get_principal(krb5_context context,
480 krb5_ccache id,
481 krb5_principal *principal)
483 krb5_dcache *dc = DCACHE(id);
484 return krb5_cc_get_principal(context, D2FCACHE(dc), principal);
487 static krb5_error_code KRB5_CALLCONV
488 dcc_get_first (krb5_context context,
489 krb5_ccache id,
490 krb5_cc_cursor *cursor)
492 krb5_dcache *dc = DCACHE(id);
493 return krb5_cc_start_seq_get(context, D2FCACHE(dc), cursor);
496 static krb5_error_code KRB5_CALLCONV
497 dcc_get_next (krb5_context context,
498 krb5_ccache id,
499 krb5_cc_cursor *cursor,
500 krb5_creds *creds)
502 krb5_dcache *dc = DCACHE(id);
503 return krb5_cc_next_cred(context, D2FCACHE(dc), cursor, creds);
506 static krb5_error_code KRB5_CALLCONV
507 dcc_end_get (krb5_context context,
508 krb5_ccache id,
509 krb5_cc_cursor *cursor)
511 krb5_dcache *dc = DCACHE(id);
512 return krb5_cc_end_seq_get(context, D2FCACHE(dc), cursor);
515 static krb5_error_code KRB5_CALLCONV
516 dcc_remove_cred(krb5_context context,
517 krb5_ccache id,
518 krb5_flags which,
519 krb5_creds *cred)
521 krb5_dcache *dc = DCACHE(id);
522 return krb5_cc_remove_cred(context, D2FCACHE(dc), which, cred);
525 static krb5_error_code KRB5_CALLCONV
526 dcc_set_flags(krb5_context context,
527 krb5_ccache id,
528 krb5_flags flags)
530 krb5_dcache *dc = DCACHE(id);
531 return krb5_cc_set_flags(context, D2FCACHE(dc), flags);
534 static int KRB5_CALLCONV
535 dcc_get_version(krb5_context context,
536 krb5_ccache id)
538 krb5_dcache *dc = DCACHE(id);
539 return krb5_cc_get_version(context, D2FCACHE(dc));
542 struct dcache_iter {
543 int first;
544 krb5_dcache *dc;
547 static krb5_error_code KRB5_CALLCONV
548 dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
550 struct dcache_iter *iter;
551 krb5_error_code ret;
552 char *name;
554 *cursor = NULL;
555 iter = calloc(1, sizeof(*iter));
556 if (iter == NULL)
557 return krb5_enomem(context);
558 iter->first = 1;
560 name = copy_default_dcc_cache(context);
561 if (name == NULL) {
562 free(iter);
563 krb5_set_error_message(context, KRB5_CC_FORMAT,
564 N_("Can't generate DIR caches unless its the default type", ""));
565 return KRB5_CC_FORMAT;
568 ret = dcc_resolve(context, NULL, name);
569 free(name);
570 if (ret) {
571 free(iter);
572 return ret;
575 /* XXX We need to opendir() here */
577 *cursor = iter;
578 return 0;
581 static krb5_error_code KRB5_CALLCONV
582 dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
584 struct dcache_iter *iter = cursor;
586 if (iter == NULL)
587 return krb5_einval(context, 2);
589 if (!iter->first) {
590 krb5_clear_error_message(context);
591 return KRB5_CC_END;
594 /* XXX We need to readdir() here */
595 iter->first = 0;
597 return KRB5_CC_END;
600 static krb5_error_code KRB5_CALLCONV
601 dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
603 struct dcache_iter *iter = cursor;
605 if (iter == NULL)
606 return krb5_einval(context, 2);
608 /* XXX We need to closedir() here */
609 if (iter->dc)
610 dcc_release(context, iter->dc);
611 free(iter);
612 return 0;
615 static krb5_error_code KRB5_CALLCONV
616 dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
618 krb5_dcache *dcfrom = DCACHE(from);
619 krb5_dcache *dcto = DCACHE(to);
620 return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
623 static krb5_error_code KRB5_CALLCONV
624 dcc_get_default_name(krb5_context context, char **str)
626 return _krb5_expand_default_cc_name(context,
627 KRB5_DEFAULT_CCNAME_DIR,
628 str);
631 static krb5_error_code KRB5_CALLCONV
632 dcc_set_default(krb5_context context, krb5_ccache id)
634 krb5_dcache *dc = DCACHE(id);
635 const char *name;
637 name = krb5_cc_get_name(context, D2FCACHE(dc));
638 if (name == NULL)
639 return ENOENT;
641 return set_default_cache(context, dc, name);
644 static krb5_error_code KRB5_CALLCONV
645 dcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
647 krb5_dcache *dc = DCACHE(id);
648 return krb5_cc_last_change_time(context, D2FCACHE(dc), mtime);
651 static krb5_error_code KRB5_CALLCONV
652 dcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
654 krb5_dcache *dc = DCACHE(id);
655 return krb5_cc_set_kdc_offset(context, D2FCACHE(dc), kdc_offset);
658 static krb5_error_code KRB5_CALLCONV
659 dcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
661 krb5_dcache *dc = DCACHE(id);
662 return krb5_cc_get_kdc_offset(context, D2FCACHE(dc), kdc_offset);
667 * Variable containing the DIR based credential cache implemention.
669 * @ingroup krb5_ccache
672 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops = {
673 KRB5_CC_OPS_VERSION,
674 "DIR",
675 dcc_get_name,
676 dcc_resolve,
677 dcc_gen_new,
678 dcc_initialize,
679 dcc_destroy,
680 dcc_close,
681 dcc_store_cred,
682 NULL, /* dcc_retrieve */
683 dcc_get_principal,
684 dcc_get_first,
685 dcc_get_next,
686 dcc_end_get,
687 dcc_remove_cred,
688 dcc_set_flags,
689 dcc_get_version,
690 dcc_get_cache_first,
691 dcc_get_cache_next,
692 dcc_end_cache_get,
693 dcc_move,
694 dcc_get_default_name,
695 dcc_set_default,
696 dcc_lastchange,
697 dcc_set_kdc_offset,
698 dcc_get_kdc_offset