2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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
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
36 #include "krb5_locl.h"
38 typedef struct 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 **);
52 primary_create(krb5_dcache
*dc
)
56 asprintf(&primary
, "%s/primary", dc
->dir
);
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
;
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
);
87 return krb5_enomem(context
);
96 if (fchmod(fd
, S_IRUSR
| S_IWUSR
) < 0) {
101 len
= strlen(residual
);
103 iov
[0].iov_base
= rk_UNCONST(residual
);
104 iov
[0].iov_len
= len
;
105 iov
[1].iov_base
= "\n";
108 if (writev(fd
, iov
, sizeof(iov
)/sizeof(iov
[0])) != len
+ 1) {
113 primary
= primary_create(dc
);
114 if (primary
== NULL
) {
115 ret
= krb5_enomem(context
);
119 if (rename(path
, primary
) < 0) {
141 static krb5_error_code
142 get_default_cache(krb5_context context
, krb5_dcache
*dc
, char **residual
)
145 char buf
[MAXPATHLEN
];
150 primary
= primary_create(dc
);
152 return krb5_enomem(context
);
154 f
= fopen(primary
, "r");
156 if (errno
== ENOENT
) {
158 *residual
= strdup("tkt");
159 if (*residual
== NULL
)
160 return krb5_enomem(context
);
164 krb5_set_error_message(context
, ret
, "failed to open %s", primary
);
169 if (fgets(buf
, sizeof(buf
), f
) == NULL
) {
172 krb5_set_error_message(context
, ret
, "read file %s", primary
);
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
);
184 return KRB5_CC_FORMAT
;
189 *residual
= strdup(buf
);
190 if (*residual
== NULL
)
191 return krb5_enomem(context
);
198 static const char* KRB5_CALLCONV
199 dcc_get_name(krb5_context context
,
202 krb5_dcache
*dc
= DCACHE(id
);
207 static krb5_error_code
208 verify_directory(krb5_context context
, const char *path
)
212 if (stat(path
, &sb
) != 0) {
213 if (errno
== ENOENT
) {
214 /* XXX should use mkdirx_np() */
215 if (rk_mkdir(path
, S_IRWXU
) == 0)
218 krb5_set_error_message(context
, ENOENT
,
219 N_("DIR directory %s doesn't exists", ""), path
);
223 krb5_set_error_message(context
, ret
,
224 N_("DIR directory %s is bad: %s", ""), path
, strerror(ret
));
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
;
238 dcc_release(krb5_context context
, krb5_dcache
*dc
)
241 krb5_cc_close(context
, dc
->fcache
);
246 memset(dc
, 0, sizeof(*dc
));
250 static krb5_error_code KRB5_CALLCONV
251 dcc_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
253 char *filename
= NULL
;
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
;
270 dc
= calloc(1, sizeof(*dc
));
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 */
281 dc
->dir
= strdup(&res
[1]);
283 q
= strrchr(dc
->dir
, '\\');
286 q
= strrchr(dc
->dir
, '/');
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
);
304 dcc_release(context
, dc
);
308 dc
->name
= strdup(res
);
309 if (dc
->name
== NULL
) {
310 dcc_release(context
, dc
);
311 return krb5_enomem(context
);
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
);
331 dcc_release(context
, dc
);
335 ret
= get_default_cache(context
, dc
, &residual
);
337 dcc_release(context
, dc
);
340 asprintf(&dc
->name
, ":%s/%s", dc
->dir
, 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
);
357 dcc_release(context
, dc
);
362 (*id
)->data
.data
= dc
;
363 (*id
)->data
.length
= sizeof(*dc
);
368 copy_default_dcc_cache(krb5_context context
)
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 ||
382 ret
= dcc_get_default_name(context
, &name
);
388 return strdup(&defname
[len
+ 1]);
393 static krb5_error_code KRB5_CALLCONV
394 dcc_gen_new(krb5_context context
, krb5_ccache
*id
)
402 name
= copy_default_dcc_cache(context
);
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
] == ':')
415 ret
= dcc_resolve(context
, id
, name
+ len
);
423 asprintf(&name
, ":%s/tktXXXXXX", dc
->dir
);
425 dcc_close(context
, *id
);
426 return krb5_enomem(context
);
429 fd
= mkstemp(&name
[1]);
431 dcc_close(context
, *id
);
432 return krb5_enomem(context
);
442 static krb5_error_code KRB5_CALLCONV
443 dcc_initialize(krb5_context context
,
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
,
455 dcc_release(context
, DCACHE(id
));
459 static krb5_error_code KRB5_CALLCONV
460 dcc_destroy(krb5_context context
,
463 krb5_dcache
*dc
= DCACHE(id
);
464 krb5_ccache fcache
= D2FCACHE(dc
);
466 return krb5_cc_destroy(context
, fcache
);
469 static krb5_error_code KRB5_CALLCONV
470 dcc_store_cred(krb5_context context
,
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
,
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
,
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
,
499 krb5_cc_cursor
*cursor
,
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
,
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
,
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
,
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
,
538 krb5_dcache
*dc
= DCACHE(id
);
539 return krb5_cc_get_version(context
, D2FCACHE(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
;
555 iter
= calloc(1, sizeof(*iter
));
557 return krb5_enomem(context
);
560 name
= copy_default_dcc_cache(context
);
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
);
575 /* XXX We need to opendir() here */
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
;
587 return krb5_einval(context
, 2);
590 krb5_clear_error_message(context
);
594 /* XXX We need to readdir() here */
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
;
606 return krb5_einval(context
, 2);
608 /* XXX We need to closedir() here */
610 dcc_release(context
, iter
->dc
);
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
,
631 static krb5_error_code KRB5_CALLCONV
632 dcc_set_default(krb5_context context
, krb5_ccache id
)
634 krb5_dcache
*dc
= DCACHE(id
);
637 name
= krb5_cc_get_name(context
, D2FCACHE(dc
));
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
= {
682 NULL
, /* dcc_retrieve */
694 dcc_get_default_name
,