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
];
149 primary
= primary_create(dc
);
151 return krb5_enomem(context
);
153 f
= fopen(primary
, "r");
155 if (errno
== ENOENT
) {
157 *residual
= strdup("tkt");
158 if (*residual
== NULL
)
159 return krb5_enomem(context
);
163 krb5_set_error_message(context
, ret
, "failed to open %s", primary
);
168 if (fgets(buf
, sizeof(buf
), f
) == NULL
) {
171 krb5_set_error_message(context
, ret
, "read file %s", primary
);
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
);
183 return KRB5_CC_FORMAT
;
188 *residual
= strdup(buf
);
189 if (*residual
== NULL
)
190 return krb5_enomem(context
);
197 static const char* KRB5_CALLCONV
198 dcc_get_name(krb5_context context
,
201 krb5_dcache
*dc
= DCACHE(id
);
206 static krb5_error_code
207 verify_directory(krb5_context context
, const char *path
)
211 if (stat(path
, &sb
) != 0) {
212 if (errno
== ENOENT
) {
213 /* XXX should use mkdirx_np() */
214 if (rk_mkdir(path
, S_IRWXU
) == 0)
217 krb5_set_error_message(context
, ENOENT
,
218 N_("DIR directory %s doesn't exists", ""), path
);
222 krb5_set_error_message(context
, ret
,
223 N_("DIR directory %s is bad: %s", ""), path
, strerror(ret
));
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
;
237 dcc_release(krb5_context context
, krb5_dcache
*dc
)
240 krb5_cc_close(context
, dc
->fcache
);
245 memset(dc
, 0, sizeof(*dc
));
249 static krb5_error_code KRB5_CALLCONV
250 dcc_resolve(krb5_context context
, krb5_ccache
*id
, const char *res
)
252 char *filename
= NULL
;
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
;
269 dc
= calloc(1, sizeof(*dc
));
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 */
280 dc
->dir
= strdup(&res
[1]);
282 q
= strrchr(dc
->dir
, '\\');
285 q
= strrchr(dc
->dir
, '/');
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
);
303 dcc_release(context
, dc
);
307 dc
->name
= strdup(res
);
308 if (dc
->name
== NULL
) {
309 dcc_release(context
, dc
);
310 return krb5_enomem(context
);
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
);
330 dcc_release(context
, dc
);
334 ret
= get_default_cache(context
, dc
, &residual
);
336 dcc_release(context
, dc
);
339 asprintf(&dc
->name
, ":%s/%s", dc
->dir
, 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
);
356 dcc_release(context
, dc
);
361 (*id
)->data
.data
= dc
;
362 (*id
)->data
.length
= sizeof(*dc
);
367 copy_default_dcc_cache(krb5_context context
)
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 ||
381 ret
= dcc_get_default_name(context
, &name
);
387 return strdup(&defname
[len
+ 1]);
392 static krb5_error_code KRB5_CALLCONV
393 dcc_gen_new(krb5_context context
, krb5_ccache
*id
)
401 name
= copy_default_dcc_cache(context
);
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
] == ':')
414 ret
= dcc_resolve(context
, id
, name
+ len
);
422 asprintf(&name
, ":%s/tktXXXXXX", dc
->dir
);
424 dcc_close(context
, *id
);
425 return krb5_enomem(context
);
429 fd
= mkstemp(&name
[1]);
431 dcc_close(context
, *id
);
432 return krb5_enomem(context
);
441 static krb5_error_code KRB5_CALLCONV
442 dcc_initialize(krb5_context context
,
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
,
454 dcc_release(context
, DCACHE(id
));
458 static krb5_error_code KRB5_CALLCONV
459 dcc_destroy(krb5_context context
,
462 krb5_dcache
*dc
= DCACHE(id
);
463 krb5_ccache fcache
= D2FCACHE(dc
);
465 return krb5_cc_destroy(context
, fcache
);
468 static krb5_error_code KRB5_CALLCONV
469 dcc_store_cred(krb5_context context
,
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
,
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
,
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
,
498 krb5_cc_cursor
*cursor
,
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
,
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
,
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
,
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
,
537 krb5_dcache
*dc
= DCACHE(id
);
538 return krb5_cc_get_version(context
, D2FCACHE(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
;
552 iter
= calloc(1, sizeof(*iter
));
554 return krb5_enomem(context
);
557 name
= copy_default_dcc_cache(context
);
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
;
566 return KRB5_CC_FORMAT
;
570 ret
= dcc_resolve(context
, NULL
, name
);
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
;
584 return krb5_einval(context
, 2);
587 krb5_clear_error_message(context
);
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
;
601 return krb5_einval(context
, 2);
604 dcc_release(context
, iter
->dc
);
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
,
625 static krb5_error_code KRB5_CALLCONV
626 dcc_set_default(krb5_context context
, krb5_ccache id
)
628 krb5_dcache
*dc
= DCACHE(id
);
631 name
= krb5_cc_get_name(context
, D2FCACHE(dc
));
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
= {
676 NULL
, /* dcc_retrieve */
688 dcc_get_default_name
,