Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
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.
18 : *
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.
22 : *
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.
34 : */
35 :
36 : #include "krb5_locl.h"
37 :
38 : typedef struct krb5_dcache{
39 : krb5_ccache fcache;
40 : char *name;
41 : char *dir;
42 : char *sub;
43 : unsigned int default_candidate:1;
44 : } krb5_dcache;
45 :
46 : #define DCACHE(X) ((krb5_dcache*)(X)->data.data)
47 : #define D2FCACHE(X) ((X)->fcache)
48 :
49 : static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache);
50 : static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **);
51 : static krb5_error_code KRB5_CALLCONV dcc_set_default(krb5_context, krb5_ccache);
52 :
53 : /*
54 : * Make subsidiary filesystem safe by mapping / and : to -. If the subsidiary
55 : * is longer than 128 bytes, then truncate.
56 : * In all cases, "tkt." is prefixed to be compatible with the DIR requirement
57 : * that subsidiary ccache files be named tkt*.
58 : *
59 : * Thus host/foo.bar.baz@BAR.BAZ -> tkt.host-foo.bar.baz@BAR.BAZ.
60 : *
61 : * In particular, no filesystem component separators will be emitted, and . and
62 : * .. will never be traversed.
63 : */
64 : static krb5_error_code
65 0 : fs_encode_subsidiary(krb5_context context,
66 : krb5_dcache *dc,
67 : const char *subsidiary,
68 : char **res)
69 : {
70 0 : size_t len = strlen(subsidiary);
71 0 : size_t i;
72 :
73 0 : *res = NULL;
74 0 : if (asprintf(res, "tkt.%s", subsidiary) == -1 || *res == NULL)
75 0 : return krb5_enomem(context);
76 0 : for (i = sizeof("tkt.") - 1; i < len; i++) {
77 0 : switch ((*res)[i]) {
78 : #ifdef WIN32
79 : case '\\': (*res)[0] = '-'; break;
80 : #endif
81 0 : case '/': (*res)[0] = '-'; break;
82 0 : case ':': (*res)[0] = '-'; break;
83 0 : default: break;
84 : }
85 : }
86 :
87 : /* Hopefully this will work on all filesystems */
88 0 : if (len > 128 - sizeof("tkt.") - 1)
89 0 : (*res)[127] = '\0';
90 0 : return 0;
91 : }
92 :
93 : static char *
94 0 : primary_create(krb5_dcache *dc)
95 : {
96 0 : char *primary = NULL;
97 0 : int asprintf_ret = asprintf(&primary, "%s/primary", dc->dir);
98 0 : if (asprintf_ret == -1 || primary == NULL) {
99 0 : return NULL;
100 : }
101 :
102 0 : return primary;
103 : }
104 :
105 : static int
106 0 : is_filename_cacheish(const char *name)
107 : {
108 0 : size_t i;
109 :
110 0 : if (strncmp(name, "tkt", sizeof("tkt") - 1) != 0)
111 0 : return 0;
112 0 : for (i = sizeof("tkt") - 1; name[i]; i++)
113 0 : if (ISPATHSEP(name[i]))
114 0 : return 0;
115 0 : return 1;
116 : }
117 :
118 : static krb5_error_code
119 0 : set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual)
120 : {
121 0 : char *path = NULL, *primary = NULL;
122 0 : krb5_error_code ret;
123 0 : struct iovec iov[2];
124 0 : size_t len;
125 0 : int fd = -1;
126 0 : int asprintf_ret;
127 :
128 0 : asprintf_ret = asprintf(&path, "%s/primary-XXXXXX", dc->dir);
129 0 : if (asprintf_ret == -1 || path == NULL) {
130 0 : return krb5_enomem(context);
131 : }
132 :
133 0 : fd = mkstemp(path);
134 0 : if (fd < 0) {
135 0 : ret = errno;
136 0 : goto out;
137 : }
138 0 : rk_cloexec(fd);
139 : #ifndef _WIN32
140 0 : if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
141 0 : ret = errno;
142 0 : goto out;
143 : }
144 : #endif
145 0 : len = strlen(residual);
146 :
147 0 : iov[0].iov_base = rk_UNCONST(residual);
148 0 : iov[0].iov_len = len;
149 0 : iov[1].iov_base = "\n";
150 0 : iov[1].iov_len = 1;
151 :
152 0 : if (writev(fd, iov, sizeof(iov)/sizeof(iov[0])) != len + 1) {
153 0 : ret = errno;
154 0 : goto out;
155 : }
156 :
157 0 : primary = primary_create(dc);
158 0 : if (primary == NULL) {
159 0 : ret = krb5_enomem(context);
160 0 : goto out;
161 : }
162 :
163 0 : if (rename(path, primary) < 0) {
164 0 : ret = errno;
165 0 : goto out;
166 : }
167 :
168 0 : close(fd);
169 0 : fd = -1;
170 :
171 0 : ret = 0;
172 0 : out:
173 0 : if (fd >= 0) {
174 0 : (void)unlink(path);
175 0 : close(fd);
176 : }
177 0 : if (path)
178 0 : free(path);
179 0 : if (primary)
180 0 : free(primary);
181 :
182 0 : return ret;
183 : }
184 :
185 : static krb5_error_code
186 0 : get_default_cache(krb5_context context, krb5_dcache *dc,
187 : const char *subsidiary, char **residual)
188 : {
189 0 : krb5_error_code ret;
190 0 : char buf[MAXPATHLEN];
191 0 : char *primary = NULL;
192 0 : FILE *f;
193 :
194 0 : *residual = NULL;
195 0 : if (subsidiary)
196 0 : return fs_encode_subsidiary(context, dc, subsidiary, residual);
197 :
198 0 : primary = primary_create(dc);
199 0 : if (primary == NULL)
200 0 : return krb5_enomem(context);
201 :
202 0 : f = fopen(primary, "r");
203 0 : if (f == NULL) {
204 0 : if (errno == ENOENT) {
205 0 : free(primary);
206 0 : *residual = strdup("tkt");
207 0 : if (*residual == NULL)
208 0 : return krb5_enomem(context);
209 0 : return 0;
210 : }
211 0 : ret = errno;
212 0 : krb5_set_error_message(context, ret, "failed to open %s", primary);
213 0 : free(primary);
214 0 : return ret;
215 : }
216 :
217 0 : if (fgets(buf, sizeof(buf), f) == NULL) {
218 0 : ret = ferror(f);
219 0 : fclose(f);
220 0 : krb5_set_error_message(context, ret, "read file %s", primary);
221 0 : free(primary);
222 0 : return ret;
223 : }
224 0 : fclose(f);
225 :
226 0 : buf[strcspn(buf, "\r\n")] = '\0';
227 :
228 0 : if (!is_filename_cacheish(buf)) {
229 0 : krb5_set_error_message(context, KRB5_CC_FORMAT,
230 : "name in %s is not a cache (doesn't start with tkt)", primary);
231 0 : free(primary);
232 0 : return KRB5_CC_FORMAT;
233 : }
234 :
235 0 : free(primary);
236 :
237 0 : *residual = strdup(buf);
238 0 : if (*residual == NULL)
239 0 : return krb5_enomem(context);
240 :
241 0 : return 0;
242 : }
243 :
244 :
245 :
246 : static krb5_error_code KRB5_CALLCONV
247 0 : dcc_get_name_2(krb5_context context,
248 : krb5_ccache id,
249 : const char **name,
250 : const char **dir,
251 : const char **sub)
252 : {
253 0 : krb5_dcache *dc = DCACHE(id);
254 :
255 0 : if (name)
256 0 : *name = dc->name;
257 0 : if (dir)
258 0 : *dir = dc->dir;
259 0 : if (sub)
260 0 : *sub = dc->sub;
261 0 : return 0;
262 : }
263 :
264 :
265 : static krb5_error_code
266 0 : verify_directory(krb5_context context, const char *path)
267 : {
268 0 : struct stat sb;
269 :
270 0 : if (!path[0]) {
271 0 : krb5_set_error_message(context, EINVAL,
272 0 : N_("DIR empty directory component", ""));
273 0 : return EINVAL;
274 : }
275 :
276 : /* XXX should use mkdirx_np() */
277 0 : if (rk_mkdir(path, S_IRWXU) == 0)
278 0 : return 0;
279 :
280 0 : if (stat(path, &sb) != 0) {
281 0 : if (errno == ENOENT) {
282 0 : krb5_set_error_message(context, ENOENT,
283 0 : N_("DIR directory %s doesn't exists", ""), path);
284 0 : return ENOENT;
285 : } else {
286 0 : krb5_set_error_message(context, errno,
287 0 : N_("DIR directory %s is bad: %s", ""), path, strerror(errno));
288 0 : return errno;
289 : }
290 : }
291 0 : if (!S_ISDIR(sb.st_mode)) {
292 0 : krb5_set_error_message(context, KRB5_CC_BADNAME,
293 0 : N_("DIR directory %s is not a directory", ""), path);
294 0 : return KRB5_CC_BADNAME;
295 : }
296 :
297 0 : return 0;
298 : }
299 :
300 : static void
301 0 : dcc_release(krb5_context context, krb5_dcache *dc)
302 : {
303 0 : if (dc->fcache)
304 0 : krb5_cc_close(context, dc->fcache);
305 0 : free(dc->sub);
306 0 : free(dc->dir);
307 0 : free(dc->name);
308 0 : memset(dc, 0, sizeof(*dc));
309 0 : free(dc);
310 0 : }
311 :
312 : static krb5_error_code
313 0 : get_default_dir(krb5_context context, char **res)
314 : {
315 0 : krb5_error_code ret;
316 0 : char *s;
317 :
318 0 : if ((ret = dcc_get_default_name(context, &s)))
319 0 : return ret;
320 0 : if (strncmp(s, "DIR:", sizeof("DIR:") - 1) != 0) {
321 0 : *res = s;
322 0 : s = NULL;
323 0 : } else if ((*res = strdup(s + sizeof("DIR:") - 1)) == NULL) {
324 0 : ret = krb5_enomem(context);
325 : }
326 0 : free(s);
327 0 : return ret;
328 : }
329 :
330 : static krb5_error_code KRB5_CALLCONV
331 0 : dcc_resolve_2(krb5_context context,
332 : krb5_ccache *id,
333 : const char *res,
334 : const char *sub)
335 : {
336 0 : krb5_error_code ret;
337 0 : krb5_dcache *dc = NULL;
338 0 : char *filename = NULL;
339 0 : size_t len;
340 0 : int has_pathsep = 0;
341 :
342 0 : if (sub) {
343 : /*
344 : * Here `res' has the directory name (or, if NULL, refers to the
345 : * default DIR cccol), and `sub' has the "subsidiary" name, to which
346 : * we'll prefix "tkt." (though we will insist only on "tkt" later).
347 : */
348 0 : if ((dc = calloc(1, sizeof(*dc))) == NULL ||
349 0 : asprintf(&dc->sub, "tkt.%s", sub) == -1 || dc->sub == NULL) {
350 0 : free(dc);
351 0 : return krb5_enomem(context);
352 : }
353 0 : if (res && res[0] && (dc->dir = strdup(res)) == NULL) {
354 0 : free(dc->sub);
355 0 : free(dc);
356 0 : return krb5_enomem(context);
357 0 : } else if ((!res || !res[0]) && (ret = get_default_dir(context, &dc->dir))) {
358 0 : free(dc->sub);
359 0 : free(dc);
360 0 : return ret;
361 : }
362 : } else {
363 0 : const char *p;
364 0 : int is_drive_letter_colon = 0;
365 :
366 : /*
367 : * Here `res' has whatever string followed "DIR:", and we need to parse
368 : * it into `dc->dir' and `dc->sub'.
369 : *
370 : * Conventions we support for DIR cache naming:
371 : *
372 : * - DIR:path:NAME ---> FILE:path/tktNAME
373 : * - DIR::path/tktNAME ---> FILE:path/tktNAME
374 : * - DIR::NAME ---> FILE:${default_DIR_cccol_path}/tktNAME
375 : * \-> FILE:/tmp/krb5cc_${uid}_dir/tktNAME
376 : * - DIR:path ---> FILE:path/$(cat primary) or FILE:path/tkt
377 : *
378 : */
379 :
380 0 : if (res == NULL || *res == '\0' || (res[0] == ':' && res[1] == '\0')) {
381 : /* XXX Why not? */
382 0 : krb5_set_error_message(context, KRB5_CC_FORMAT,
383 0 : N_("\"DIR:\" is not a valid ccache name", ""));
384 0 : return KRB5_CC_FORMAT;
385 : }
386 :
387 : #ifdef WIN32
388 : has_pathsep = strchr(res, '\\') != NULL;
389 : #endif
390 0 : has_pathsep |= strchr(res, '/') != NULL;
391 :
392 0 : if ((dc = calloc(1, sizeof(*dc))) == NULL)
393 0 : return krb5_enomem(context);
394 :
395 0 : p = strrchr(res, ':');
396 : #ifdef WIN32
397 : is_drive_letter_colon =
398 : p && ((res[0] == ':' && res[1] != ':' && p - res == 2) ||
399 : (res[0] != ':' && p - res == 1));
400 : #endif
401 :
402 0 : if (res[0] != ':' && p && !is_drive_letter_colon) {
403 : /* DIR:path:NAME */
404 0 : if ((dc->dir = strndup(res, (p - res))) == NULL ||
405 0 : asprintf(&dc->sub, "tkt.%s", p + 1) < 0 || dc->sub == NULL) {
406 0 : dcc_release(context, dc);
407 0 : return krb5_enomem(context);
408 : }
409 0 : } else if (res[0] == ':' && has_pathsep) {
410 0 : char *q;
411 :
412 : /* DIR::path/tktNAME (the "tkt" must be there; we'll check) */
413 0 : if ((dc->dir = strdup(&res[1])) == NULL) {
414 0 : dcc_release(context, dc);
415 0 : return krb5_enomem(context);
416 : }
417 : #ifdef _WIN32
418 : q = strrchr(dc->dir, '\\');
419 : if (q == NULL || ((p = strrchr(dc->dir, '/')) && q < p))
420 : #endif
421 0 : q = strrchr(dc->dir, '/');
422 0 : *q++ = '\0';
423 0 : if ((dc->sub = strdup(q)) == NULL) {
424 0 : dcc_release(context, dc);
425 0 : return krb5_enomem(context);
426 : }
427 0 : } else if (res[0] == ':') {
428 : /* DIR::NAME -- no path component separators in NAME */
429 0 : if ((ret = get_default_dir(context, &dc->dir))) {
430 0 : dcc_release(context, dc);
431 0 : return ret;
432 : }
433 0 : if (asprintf(&dc->sub, "tkt.%s", res + 1) < 0 || dc->sub == NULL) {
434 0 : dcc_release(context, dc);
435 0 : return krb5_enomem(context);
436 : }
437 : } else {
438 : /* DIR:path */
439 0 : if ((dc->dir = strdup(res)) == NULL) {
440 0 : dcc_release(context, dc);
441 0 : return krb5_enomem(context);
442 : }
443 :
444 0 : if ((ret = get_default_cache(context, dc, NULL, &dc->sub))) {
445 0 : dcc_release(context, dc);
446 0 : return ret;
447 : }
448 : }
449 : }
450 :
451 : /* Strip off extra slashes on the end */
452 0 : for (len = strlen(dc->dir);
453 0 : len && ISPATHSEP(dc->dir[len - 1]);
454 0 : len--)
455 0 : dc->dir[len - 1] = '\0';
456 :
457 : /* If we got here then `dc->dir' and `dc->sub' must both be set */
458 :
459 0 : if ((ret = verify_directory(context, dc->dir))) {
460 0 : dcc_release(context, dc);
461 0 : return ret;
462 : }
463 0 : if (!is_filename_cacheish(dc->sub)) {
464 0 : krb5_set_error_message(context, KRB5_CC_FORMAT,
465 0 : N_("Name %s is not a cache "
466 : "(doesn't start with tkt)", ""), dc->sub);
467 0 : dcc_release(context, dc);
468 0 : return KRB5_CC_FORMAT;
469 : }
470 0 : if (asprintf(&dc->name, ":%s/%s", dc->dir, dc->sub) == -1 ||
471 0 : dc->name == NULL ||
472 0 : asprintf(&filename, "FILE%s", dc->name) == -1 || filename == NULL) {
473 0 : dcc_release(context, dc);
474 0 : return krb5_enomem(context);
475 : }
476 :
477 0 : ret = krb5_cc_resolve(context, filename, &dc->fcache);
478 0 : free(filename);
479 0 : if (ret) {
480 0 : dcc_release(context, dc);
481 0 : return ret;
482 : }
483 :
484 0 : dc->default_candidate = 1;
485 0 : (*id)->data.data = dc;
486 0 : (*id)->data.length = sizeof(*dc);
487 0 : return 0;
488 : }
489 :
490 : static krb5_error_code KRB5_CALLCONV
491 0 : dcc_gen_new(krb5_context context, krb5_ccache *id)
492 : {
493 0 : krb5_error_code ret;
494 0 : char *def_dir = NULL;
495 0 : char *name = NULL;
496 0 : int fd = -1;
497 :
498 0 : ret = get_default_dir(context, &def_dir);
499 0 : if (ret == 0)
500 0 : ret = verify_directory(context, def_dir);
501 0 : if (ret == 0 &&
502 0 : (asprintf(&name, "DIR::%s/tktXXXXXX", def_dir) == -1 || name == NULL))
503 0 : ret = krb5_enomem(context);
504 0 : if (ret == 0 && (fd = mkstemp(name + sizeof("DIR::") - 1)) == -1)
505 0 : ret = errno;
506 0 : if (ret == 0)
507 0 : ret = dcc_resolve_2(context, id, name + sizeof("DIR:") - 1, NULL);
508 :
509 0 : free(def_dir);
510 0 : free(name);
511 0 : if (fd != -1)
512 0 : close(fd);
513 0 : return ret;
514 : }
515 :
516 : static krb5_error_code KRB5_CALLCONV
517 0 : dcc_initialize(krb5_context context,
518 : krb5_ccache id,
519 : krb5_principal primary_principal)
520 : {
521 0 : krb5_dcache *dc = DCACHE(id);
522 0 : return krb5_cc_initialize(context, D2FCACHE(dc), primary_principal);
523 : }
524 :
525 : static krb5_error_code KRB5_CALLCONV
526 0 : dcc_close(krb5_context context,
527 : krb5_ccache id)
528 : {
529 0 : krb5_dcache *dc = DCACHE(id);
530 0 : krb5_principal p = NULL;
531 0 : struct stat st;
532 0 : char *primary = NULL;
533 :
534 : /*
535 : * If there's no default cache, but we're closing one, and the one we're
536 : * closing has been initialized, then make it the default. This makes the
537 : * first cache created the default.
538 : *
539 : * FIXME We should check if `D2FCACHE(dc)' has live credentials.
540 : */
541 0 : if (dc->default_candidate && D2FCACHE(dc) &&
542 0 : krb5_cc_get_principal(context, D2FCACHE(dc), &p) == 0 &&
543 0 : (primary = primary_create(dc)) &&
544 0 : (stat(primary, &st) == -1 || !S_ISREG(st.st_mode) || st.st_size == 0))
545 0 : dcc_set_default(context, id);
546 0 : krb5_free_principal(context, p);
547 0 : free(primary);
548 0 : dcc_release(context, DCACHE(id));
549 0 : return 0;
550 : }
551 :
552 : static krb5_error_code KRB5_CALLCONV
553 0 : dcc_destroy(krb5_context context,
554 : krb5_ccache id)
555 : {
556 0 : krb5_dcache *dc = DCACHE(id);
557 0 : krb5_ccache fcache = D2FCACHE(dc);
558 0 : dc->fcache = NULL;
559 0 : return krb5_cc_destroy(context, fcache);
560 : }
561 :
562 : static krb5_error_code KRB5_CALLCONV
563 0 : dcc_store_cred(krb5_context context,
564 : krb5_ccache id,
565 : krb5_creds *creds)
566 : {
567 0 : krb5_dcache *dc = DCACHE(id);
568 0 : return krb5_cc_store_cred(context, D2FCACHE(dc), creds);
569 : }
570 :
571 : static krb5_error_code KRB5_CALLCONV
572 0 : dcc_get_principal(krb5_context context,
573 : krb5_ccache id,
574 : krb5_principal *principal)
575 : {
576 0 : krb5_dcache *dc = DCACHE(id);
577 0 : return krb5_cc_get_principal(context, D2FCACHE(dc), principal);
578 : }
579 :
580 : static krb5_error_code KRB5_CALLCONV
581 0 : dcc_get_first (krb5_context context,
582 : krb5_ccache id,
583 : krb5_cc_cursor *cursor)
584 : {
585 0 : krb5_dcache *dc = DCACHE(id);
586 0 : return krb5_cc_start_seq_get(context, D2FCACHE(dc), cursor);
587 : }
588 :
589 : static krb5_error_code KRB5_CALLCONV
590 0 : dcc_get_next (krb5_context context,
591 : krb5_ccache id,
592 : krb5_cc_cursor *cursor,
593 : krb5_creds *creds)
594 : {
595 0 : krb5_dcache *dc = DCACHE(id);
596 0 : return krb5_cc_next_cred(context, D2FCACHE(dc), cursor, creds);
597 : }
598 :
599 : static krb5_error_code KRB5_CALLCONV
600 0 : dcc_end_get (krb5_context context,
601 : krb5_ccache id,
602 : krb5_cc_cursor *cursor)
603 : {
604 0 : krb5_dcache *dc = DCACHE(id);
605 0 : return krb5_cc_end_seq_get(context, D2FCACHE(dc), cursor);
606 : }
607 :
608 : static krb5_error_code KRB5_CALLCONV
609 0 : dcc_remove_cred(krb5_context context,
610 : krb5_ccache id,
611 : krb5_flags which,
612 : krb5_creds *cred)
613 : {
614 0 : krb5_dcache *dc = DCACHE(id);
615 0 : return krb5_cc_remove_cred(context, D2FCACHE(dc), which, cred);
616 : }
617 :
618 : static krb5_error_code KRB5_CALLCONV
619 0 : dcc_set_flags(krb5_context context,
620 : krb5_ccache id,
621 : krb5_flags flags)
622 : {
623 0 : krb5_dcache *dc = DCACHE(id);
624 0 : return krb5_cc_set_flags(context, D2FCACHE(dc), flags);
625 : }
626 :
627 : static int KRB5_CALLCONV
628 0 : dcc_get_version(krb5_context context,
629 : krb5_ccache id)
630 : {
631 0 : krb5_dcache *dc = DCACHE(id);
632 0 : return krb5_cc_get_version(context, D2FCACHE(dc));
633 : }
634 :
635 : struct dcache_iter {
636 : char *primary;
637 : krb5_dcache *dc;
638 : DIR *d;
639 : unsigned int first:1;
640 : };
641 :
642 : static krb5_error_code KRB5_CALLCONV
643 52 : dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
644 : {
645 52 : struct dcache_iter *iter = NULL;
646 52 : const char *name = krb5_cc_default_name(context);
647 0 : size_t len;
648 0 : char *p;
649 :
650 52 : *cursor = NULL;
651 :
652 52 : if (strncmp(name, "DIR:", sizeof("DIR:") - 1) != 0) {
653 52 : krb5_set_error_message(context, KRB5_CC_FORMAT,
654 52 : N_("Can't list DIR caches unless its the default type", ""));
655 52 : return KRB5_CC_FORMAT;
656 : }
657 :
658 0 : if ((iter = calloc(1, sizeof(*iter))) == NULL ||
659 0 : (iter->dc = calloc(1, sizeof(iter->dc[0]))) == NULL ||
660 0 : (iter->dc->dir = strdup(name + sizeof("DIR:") - 1)) == NULL) {
661 0 : if (iter)
662 0 : free(iter->dc);
663 0 : free(iter);
664 0 : return krb5_enomem(context);
665 : }
666 0 : iter->first = 1;
667 0 : p = strrchr(iter->dc->dir, ':');
668 : #ifdef WIN32
669 : if (p == iter->dc->dir + 1)
670 : p = NULL;
671 : #endif
672 0 : if (p)
673 0 : *p = '\0';
674 :
675 : /* Strip off extra slashes on the end */
676 0 : for (len = strlen(iter->dc->dir);
677 0 : len && ISPATHSEP(iter->dc->dir[len - 1]);
678 0 : len--) {
679 0 : iter->dc->dir[len - 1] = '\0';
680 : }
681 :
682 0 : if ((iter->d = opendir(iter->dc->dir)) == NULL) {
683 0 : krb5_set_error_message(context, KRB5_CC_FORMAT,
684 0 : N_("Can't open DIR %s: %s", ""),
685 0 : iter->dc->dir, strerror(errno));
686 0 : free(iter->dc->dir);
687 0 : free(iter->dc);
688 0 : free(iter);
689 0 : return KRB5_CC_FORMAT;
690 : }
691 :
692 0 : *cursor = iter;
693 0 : return 0;
694 : }
695 :
696 : static krb5_error_code KRB5_CALLCONV
697 0 : dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
698 : {
699 0 : struct dcache_iter *iter = cursor;
700 0 : krb5_error_code ret;
701 0 : struct stat st;
702 0 : struct dirent *dentry;
703 0 : char *p = NULL;
704 :
705 0 : *id = NULL;
706 0 : if (iter == NULL)
707 0 : return krb5_einval(context, 2);
708 :
709 : /* Emit primary subsidiary first */
710 0 : if (iter->first &&
711 0 : get_default_cache(context, iter->dc, NULL, &iter->primary) == 0 &&
712 0 : iter->primary && is_filename_cacheish(iter->primary)) {
713 0 : iter->first = 0;
714 0 : ret = KRB5_CC_END;
715 0 : if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, iter->primary) > -1 && p != NULL &&
716 0 : stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode))
717 0 : ret = krb5_cc_resolve(context, p, id);
718 0 : if (p == NULL)
719 0 : return krb5_enomem(context);
720 0 : free(p);
721 0 : if (ret == 0)
722 0 : return ret;
723 0 : p = NULL;
724 : }
725 :
726 0 : iter->first = 0;
727 0 : for (dentry = readdir(iter->d); dentry; dentry = readdir(iter->d)) {
728 0 : if (!is_filename_cacheish(dentry->d_name) ||
729 0 : (iter->primary && strcmp(dentry->d_name, iter->primary) == 0))
730 0 : continue;
731 0 : p = NULL;
732 0 : ret = KRB5_CC_END;
733 0 : if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, dentry->d_name) > -1 &&
734 0 : p != NULL &&
735 0 : stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode))
736 0 : ret = krb5_cc_resolve(context, p, id);
737 0 : free(p);
738 0 : if (p == NULL)
739 0 : return krb5_enomem(context);
740 0 : if (ret == 0)
741 0 : return ret;
742 : }
743 0 : return KRB5_CC_END;
744 : }
745 :
746 : static krb5_error_code KRB5_CALLCONV
747 0 : dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
748 : {
749 0 : struct dcache_iter *iter = cursor;
750 :
751 0 : if (iter == NULL)
752 0 : return krb5_einval(context, 2);
753 :
754 0 : (void) closedir(iter->d);
755 0 : free(iter->dc->dir);
756 0 : free(iter->dc);
757 0 : free(iter->primary);
758 0 : free(iter);
759 0 : return 0;
760 : }
761 :
762 : static krb5_error_code KRB5_CALLCONV
763 0 : dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
764 : {
765 0 : krb5_dcache *dcfrom = DCACHE(from);
766 0 : krb5_dcache *dcto = DCACHE(to);
767 :
768 0 : dcfrom->default_candidate = 0;
769 0 : dcto->default_candidate = 1;
770 0 : return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
771 : }
772 :
773 : static krb5_error_code KRB5_CALLCONV
774 0 : dcc_get_default_name(krb5_context context, char **str)
775 : {
776 0 : const char *def_cc_colname =
777 0 : krb5_config_get_string_default(context, NULL, KRB5_DEFAULT_CCNAME_DIR,
778 : "libdefaults", "default_cc_collection",
779 : NULL);
780 :
781 : /* [libdefaults] default_cc_collection is for testing */
782 0 : if (strncmp(def_cc_colname, "DIR:", sizeof("DIR:") - 1) != 0)
783 0 : def_cc_colname = KRB5_DEFAULT_CCNAME_DIR;
784 0 : return _krb5_expand_default_cc_name(context, def_cc_colname, str);
785 : }
786 :
787 : static krb5_error_code KRB5_CALLCONV
788 0 : dcc_set_default(krb5_context context, krb5_ccache id)
789 : {
790 0 : krb5_dcache *dc = DCACHE(id);
791 :
792 0 : if (dc->sub == NULL)
793 0 : return ENOENT;
794 0 : return set_default_cache(context, dc, dc->sub);
795 : }
796 :
797 : static krb5_error_code KRB5_CALLCONV
798 0 : dcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
799 : {
800 0 : krb5_dcache *dc = DCACHE(id);
801 0 : return krb5_cc_last_change_time(context, D2FCACHE(dc), mtime);
802 : }
803 :
804 : static krb5_error_code KRB5_CALLCONV
805 0 : dcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
806 : {
807 0 : krb5_dcache *dc = DCACHE(id);
808 0 : return krb5_cc_set_kdc_offset(context, D2FCACHE(dc), kdc_offset);
809 : }
810 :
811 : static krb5_error_code KRB5_CALLCONV
812 0 : dcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
813 : {
814 0 : krb5_dcache *dc = DCACHE(id);
815 0 : return krb5_cc_get_kdc_offset(context, D2FCACHE(dc), kdc_offset);
816 : }
817 :
818 :
819 : /**
820 : * Variable containing the DIR based credential cache implemention.
821 : *
822 : * @ingroup krb5_ccache
823 : */
824 :
825 : KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops = {
826 : KRB5_CC_OPS_VERSION_5,
827 : "DIR",
828 : NULL,
829 : NULL,
830 : dcc_gen_new,
831 : dcc_initialize,
832 : dcc_destroy,
833 : dcc_close,
834 : dcc_store_cred,
835 : NULL, /* dcc_retrieve */
836 : dcc_get_principal,
837 : dcc_get_first,
838 : dcc_get_next,
839 : dcc_end_get,
840 : dcc_remove_cred,
841 : dcc_set_flags,
842 : dcc_get_version,
843 : dcc_get_cache_first,
844 : dcc_get_cache_next,
845 : dcc_end_cache_get,
846 : dcc_move,
847 : dcc_get_default_name,
848 : dcc_set_default,
849 : dcc_lastchange,
850 : dcc_set_kdc_offset,
851 : dcc_get_kdc_offset,
852 : dcc_get_name_2,
853 : dcc_resolve_2
854 : };
|