xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/dcache.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1*d3273b5bSchristos /*	$NetBSD: dcache.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2b9d004c6Schristos 
3b9d004c6Schristos /*
4b9d004c6Schristos  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5b9d004c6Schristos  * (Royal Institute of Technology, Stockholm, Sweden).
6b9d004c6Schristos  * All rights reserved.
7b9d004c6Schristos  *
8b9d004c6Schristos  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9b9d004c6Schristos  *
10b9d004c6Schristos  * Redistribution and use in source and binary forms, with or without
11b9d004c6Schristos  * modification, are permitted provided that the following conditions
12b9d004c6Schristos  * are met:
13b9d004c6Schristos  *
14b9d004c6Schristos  * 1. Redistributions of source code must retain the above copyright
15b9d004c6Schristos  *    notice, this list of conditions and the following disclaimer.
16b9d004c6Schristos  *
17b9d004c6Schristos  * 2. Redistributions in binary form must reproduce the above copyright
18b9d004c6Schristos  *    notice, this list of conditions and the following disclaimer in the
19b9d004c6Schristos  *    documentation and/or other materials provided with the distribution.
20b9d004c6Schristos  *
21b9d004c6Schristos  * 3. Neither the name of the Institute nor the names of its contributors
22b9d004c6Schristos  *    may be used to endorse or promote products derived from this software
23b9d004c6Schristos  *    without specific prior written permission.
24b9d004c6Schristos  *
25b9d004c6Schristos  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26b9d004c6Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27b9d004c6Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28b9d004c6Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29b9d004c6Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30b9d004c6Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31b9d004c6Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32b9d004c6Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33b9d004c6Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34b9d004c6Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35b9d004c6Schristos  * SUCH DAMAGE.
36b9d004c6Schristos  */
37b9d004c6Schristos 
38b9d004c6Schristos #include "krb5_locl.h"
39b9d004c6Schristos 
40b9d004c6Schristos typedef struct krb5_dcache{
41b9d004c6Schristos     krb5_ccache fcache;
42b9d004c6Schristos     char *dir;
43b9d004c6Schristos     char *name;
44b9d004c6Schristos } krb5_dcache;
45b9d004c6Schristos 
46b9d004c6Schristos #define DCACHE(X) ((krb5_dcache*)(X)->data.data)
47b9d004c6Schristos #define D2FCACHE(X) ((X)->fcache)
48b9d004c6Schristos 
49b9d004c6Schristos static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache);
50b9d004c6Schristos static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **);
51b9d004c6Schristos 
52b9d004c6Schristos 
53b9d004c6Schristos static char *
primary_create(krb5_dcache * dc)54b9d004c6Schristos primary_create(krb5_dcache *dc)
55b9d004c6Schristos {
56b9d004c6Schristos     char *primary = NULL;
57b9d004c6Schristos 
58b9d004c6Schristos     asprintf(&primary, "%s/primary", dc->dir);
59b9d004c6Schristos     if (primary == NULL)
60b9d004c6Schristos 	return NULL;
61b9d004c6Schristos 
62b9d004c6Schristos     return primary;
63b9d004c6Schristos }
64b9d004c6Schristos 
65b9d004c6Schristos static int
is_filename_cacheish(const char * name)66b9d004c6Schristos is_filename_cacheish(const char *name)
67b9d004c6Schristos {
68b9d004c6Schristos     return strncmp(name, "tkt", 3) == 0;
69b9d004c6Schristos 
70b9d004c6Schristos }
71b9d004c6Schristos 
72b9d004c6Schristos static krb5_error_code
set_default_cache(krb5_context context,krb5_dcache * dc,const char * residual)73b9d004c6Schristos set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual)
74b9d004c6Schristos {
75b9d004c6Schristos     char *path = NULL, *primary = NULL;
76b9d004c6Schristos     krb5_error_code ret;
77b9d004c6Schristos     struct iovec iov[2];
78b9d004c6Schristos     size_t len;
79b9d004c6Schristos     int fd = -1;
80b9d004c6Schristos 
81b9d004c6Schristos     if (!is_filename_cacheish(residual)) {
82b9d004c6Schristos 	krb5_set_error_message(context, KRB5_CC_FORMAT,
83b9d004c6Schristos 			       "name %s is not a cache (doesn't start with tkt)", residual);
84b9d004c6Schristos 	return KRB5_CC_FORMAT;
85b9d004c6Schristos     }
86b9d004c6Schristos 
87b9d004c6Schristos     asprintf(&path, "%s/primary-XXXXXX", dc->dir);
88b9d004c6Schristos     if (path == NULL)
89b9d004c6Schristos 	return krb5_enomem(context);
90b9d004c6Schristos 
91b9d004c6Schristos     fd = mkstemp(path);
92b9d004c6Schristos     if (fd < 0) {
93b9d004c6Schristos 	ret = errno;
94b9d004c6Schristos 	goto out;
95b9d004c6Schristos     }
96b9d004c6Schristos     rk_cloexec(fd);
97b9d004c6Schristos #ifndef _WIN32
98b9d004c6Schristos     if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
99b9d004c6Schristos 	ret = errno;
100b9d004c6Schristos 	goto out;
101b9d004c6Schristos     }
102b9d004c6Schristos #endif
103b9d004c6Schristos     len = strlen(residual);
104b9d004c6Schristos 
105b9d004c6Schristos     iov[0].iov_base = rk_UNCONST(residual);
106b9d004c6Schristos     iov[0].iov_len = len;
107b9d004c6Schristos     iov[1].iov_base = "\n";
108b9d004c6Schristos     iov[1].iov_len = 1;
109b9d004c6Schristos 
110b9d004c6Schristos     if (writev(fd, iov, sizeof(iov)/sizeof(iov[0])) != len + 1) {
111b9d004c6Schristos 	ret = errno;
112b9d004c6Schristos 	goto out;
113b9d004c6Schristos     }
114b9d004c6Schristos 
115b9d004c6Schristos     primary = primary_create(dc);
116b9d004c6Schristos     if (primary == NULL) {
117b9d004c6Schristos 	ret = krb5_enomem(context);
118b9d004c6Schristos 	goto out;
119b9d004c6Schristos     }
120b9d004c6Schristos 
121b9d004c6Schristos     if (rename(path, primary) < 0) {
122b9d004c6Schristos 	ret = errno;
123b9d004c6Schristos 	goto out;
124b9d004c6Schristos     }
125b9d004c6Schristos 
126b9d004c6Schristos     close(fd);
127b9d004c6Schristos     fd = -1;
128b9d004c6Schristos 
129b9d004c6Schristos     ret = 0;
130b9d004c6Schristos  out:
131b9d004c6Schristos     if (fd >= 0) {
132b9d004c6Schristos 	(void)unlink(path);
133b9d004c6Schristos 	close(fd);
134b9d004c6Schristos     }
135b9d004c6Schristos     if (path)
136b9d004c6Schristos 	free(path);
137b9d004c6Schristos     if (primary)
138b9d004c6Schristos 	free(primary);
139b9d004c6Schristos 
140b9d004c6Schristos     return ret;
141b9d004c6Schristos }
142b9d004c6Schristos 
143b9d004c6Schristos static krb5_error_code
get_default_cache(krb5_context context,krb5_dcache * dc,char ** residual)144b9d004c6Schristos get_default_cache(krb5_context context, krb5_dcache *dc, char **residual)
145b9d004c6Schristos {
146b9d004c6Schristos     krb5_error_code ret;
147b9d004c6Schristos     char buf[MAXPATHLEN];
148b9d004c6Schristos     char *primary;
149b9d004c6Schristos     FILE *f;
150b9d004c6Schristos 
151b9d004c6Schristos     *residual = NULL;
152b9d004c6Schristos     primary = primary_create(dc);
153b9d004c6Schristos     if (primary == NULL)
154b9d004c6Schristos 	return krb5_enomem(context);
155b9d004c6Schristos 
156b9d004c6Schristos     f = fopen(primary, "r");
157b9d004c6Schristos     if (f == NULL) {
158b9d004c6Schristos 	if (errno == ENOENT) {
159b9d004c6Schristos 	    free(primary);
160b9d004c6Schristos 	    *residual = strdup("tkt");
161b9d004c6Schristos 	    if (*residual == NULL)
162b9d004c6Schristos 		return krb5_enomem(context);
163b9d004c6Schristos 	    return 0;
164b9d004c6Schristos 	}
165b9d004c6Schristos 	ret = errno;
166b9d004c6Schristos 	krb5_set_error_message(context, ret, "failed to open %s", primary);
167b9d004c6Schristos 	free(primary);
168b9d004c6Schristos 	return ret;
169b9d004c6Schristos     }
170b9d004c6Schristos 
171b9d004c6Schristos     if (fgets(buf, sizeof(buf), f) == NULL) {
172b9d004c6Schristos 	ret = ferror(f);
173b9d004c6Schristos 	fclose(f);
174b9d004c6Schristos 	krb5_set_error_message(context, ret, "read file %s", primary);
175b9d004c6Schristos 	free(primary);
176b9d004c6Schristos 	return ret;
177b9d004c6Schristos     }
178b9d004c6Schristos     fclose(f);
179b9d004c6Schristos 
180b9d004c6Schristos     buf[strcspn(buf, "\r\n")] = '\0';
181b9d004c6Schristos 
182b9d004c6Schristos     if (!is_filename_cacheish(buf)) {
183b9d004c6Schristos 	krb5_set_error_message(context, KRB5_CC_FORMAT,
184b9d004c6Schristos 			       "name in %s is not a cache (doesn't start with tkt)", primary);
185b9d004c6Schristos 	free(primary);
186b9d004c6Schristos         return KRB5_CC_FORMAT;
187b9d004c6Schristos     }
188b9d004c6Schristos 
189b9d004c6Schristos     free(primary);
190b9d004c6Schristos 
191b9d004c6Schristos     *residual = strdup(buf);
192b9d004c6Schristos     if (*residual == NULL)
193b9d004c6Schristos 	return krb5_enomem(context);
194b9d004c6Schristos 
195b9d004c6Schristos     return 0;
196b9d004c6Schristos }
197b9d004c6Schristos 
198b9d004c6Schristos 
199b9d004c6Schristos 
200b9d004c6Schristos static const char* KRB5_CALLCONV
dcc_get_name(krb5_context context,krb5_ccache id)201b9d004c6Schristos dcc_get_name(krb5_context context,
202b9d004c6Schristos 	     krb5_ccache id)
203b9d004c6Schristos {
204b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
205b9d004c6Schristos     return dc->name;
206b9d004c6Schristos }
207b9d004c6Schristos 
208b9d004c6Schristos 
209b9d004c6Schristos static krb5_error_code
verify_directory(krb5_context context,const char * path)210b9d004c6Schristos verify_directory(krb5_context context, const char *path)
211b9d004c6Schristos {
212b9d004c6Schristos     struct stat sb;
213b9d004c6Schristos 
214b9d004c6Schristos     if (stat(path, &sb) != 0) {
215b9d004c6Schristos 	if (errno == ENOENT) {
216b9d004c6Schristos 	    /* XXX should use mkdirx_np()  */
217b9d004c6Schristos 	    if (rk_mkdir(path, S_IRWXU) == 0)
218b9d004c6Schristos 		return 0;
219b9d004c6Schristos 
220b9d004c6Schristos 	    krb5_set_error_message(context, ENOENT,
221b9d004c6Schristos 				   N_("DIR directory %s doesn't exists", ""), path);
222b9d004c6Schristos 	    return ENOENT;
223b9d004c6Schristos 	} else {
224b9d004c6Schristos 	    int ret = errno;
225b9d004c6Schristos 	    krb5_set_error_message(context, ret,
226b9d004c6Schristos 				   N_("DIR directory %s is bad: %s", ""), path, strerror(ret));
227b9d004c6Schristos 	    return errno;
228b9d004c6Schristos 	}
229b9d004c6Schristos     }
230b9d004c6Schristos     if (!S_ISDIR(sb.st_mode)) {
231b9d004c6Schristos 	krb5_set_error_message(context, KRB5_CC_BADNAME,
232b9d004c6Schristos 			       N_("DIR directory %s is not a directory", ""), path);
233b9d004c6Schristos 	return KRB5_CC_BADNAME;
234b9d004c6Schristos     }
235b9d004c6Schristos 
236b9d004c6Schristos     return 0;
237b9d004c6Schristos }
238b9d004c6Schristos 
239b9d004c6Schristos static void
dcc_release(krb5_context context,krb5_dcache * dc)240b9d004c6Schristos dcc_release(krb5_context context, krb5_dcache *dc)
241b9d004c6Schristos {
242b9d004c6Schristos     if (dc->fcache)
243b9d004c6Schristos 	krb5_cc_close(context, dc->fcache);
244b9d004c6Schristos     if (dc->dir)
245b9d004c6Schristos 	free(dc->dir);
246b9d004c6Schristos     if (dc->name)
247b9d004c6Schristos 	free(dc->name);
248b9d004c6Schristos     memset(dc, 0, sizeof(*dc));
249b9d004c6Schristos     free(dc);
250b9d004c6Schristos }
251b9d004c6Schristos 
252b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_resolve(krb5_context context,krb5_ccache * id,const char * res)253b9d004c6Schristos dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
254b9d004c6Schristos {
255b9d004c6Schristos     char *filename = NULL;
256b9d004c6Schristos     krb5_error_code ret;
257b9d004c6Schristos     krb5_dcache *dc;
258b9d004c6Schristos     const char *p;
259b9d004c6Schristos 
260b9d004c6Schristos     p = res;
261b9d004c6Schristos     do {
262b9d004c6Schristos 	p = strstr(p, "..");
263b9d004c6Schristos 	if (p && (p == res || ISPATHSEP(p[-1])) && (ISPATHSEP(p[2]) || p[2] == '\0')) {
264b9d004c6Schristos 	    krb5_set_error_message(context, KRB5_CC_FORMAT,
265b9d004c6Schristos 				   N_("Path contains a .. component", ""));
266b9d004c6Schristos 	    return KRB5_CC_FORMAT;
267b9d004c6Schristos 	}
268b9d004c6Schristos 	if (p)
269b9d004c6Schristos 	    p += 3;
270b9d004c6Schristos     } while (p);
271b9d004c6Schristos 
272b9d004c6Schristos     dc = calloc(1, sizeof(*dc));
273b9d004c6Schristos     if (dc == NULL) {
274b9d004c6Schristos 	krb5_set_error_message(context, KRB5_CC_NOMEM,
275b9d004c6Schristos 			       N_("malloc: out of memory", ""));
276b9d004c6Schristos 	return KRB5_CC_NOMEM;
277b9d004c6Schristos     }
278b9d004c6Schristos 
279b9d004c6Schristos     /* check for explicit component */
280b9d004c6Schristos     if (res[0] == ':') {
281b9d004c6Schristos 	char *q;
282b9d004c6Schristos 
283b9d004c6Schristos 	dc->dir = strdup(&res[1]);
284b9d004c6Schristos #ifdef _WIN32
285b9d004c6Schristos 	q = strrchr(dc->dir, '\\');
286b9d004c6Schristos 	if (q == NULL)
287b9d004c6Schristos #endif
288b9d004c6Schristos 	q = strrchr(dc->dir, '/');
289b9d004c6Schristos 	if (q) {
290b9d004c6Schristos 	    *q++ = '\0';
291b9d004c6Schristos 	} else {
292b9d004c6Schristos 	    krb5_set_error_message(context, KRB5_CC_FORMAT, N_("Cache not an absolute path: %s", ""), dc->dir);
293b9d004c6Schristos 	    dcc_release(context, dc);
294b9d004c6Schristos 	    return KRB5_CC_FORMAT;
295b9d004c6Schristos 	}
296b9d004c6Schristos 
297b9d004c6Schristos 	if (!is_filename_cacheish(q)) {
298b9d004c6Schristos 	    krb5_set_error_message(context, KRB5_CC_FORMAT,
299b9d004c6Schristos 				   N_("Name %s is not a cache (doesn't start with tkt)", ""), q);
300b9d004c6Schristos 	    dcc_release(context, dc);
301b9d004c6Schristos 	    return KRB5_CC_FORMAT;
302b9d004c6Schristos 	}
303b9d004c6Schristos 
304b9d004c6Schristos 	ret = verify_directory(context, dc->dir);
305b9d004c6Schristos 	if (ret) {
306b9d004c6Schristos 	    dcc_release(context, dc);
307b9d004c6Schristos 	    return ret;
308b9d004c6Schristos 	}
309b9d004c6Schristos 
310b9d004c6Schristos 	dc->name = strdup(res);
311b9d004c6Schristos 	if (dc->name == NULL) {
312b9d004c6Schristos 	    dcc_release(context, dc);
313b9d004c6Schristos 	    return krb5_enomem(context);
314b9d004c6Schristos 	}
315b9d004c6Schristos 
316b9d004c6Schristos     } else {
317b9d004c6Schristos 	char *residual;
318b9d004c6Schristos 	size_t len;
319b9d004c6Schristos 
320b9d004c6Schristos 	dc->dir = strdup(res);
321b9d004c6Schristos 	if (dc->dir == NULL) {
322b9d004c6Schristos 	    dcc_release(context, dc);
323b9d004c6Schristos 	    return krb5_enomem(context);
324b9d004c6Schristos 	}
325b9d004c6Schristos 
326b9d004c6Schristos 	len = strlen(dc->dir);
327b9d004c6Schristos 
328b9d004c6Schristos 	if (ISPATHSEP(dc->dir[len - 1]))
329b9d004c6Schristos 	    dc->dir[len - 1] = '\0';
330b9d004c6Schristos 
331b9d004c6Schristos 	ret = verify_directory(context, dc->dir);
332b9d004c6Schristos 	if (ret) {
333b9d004c6Schristos 	    dcc_release(context, dc);
334b9d004c6Schristos 	    return ret;
335b9d004c6Schristos 	}
336b9d004c6Schristos 
337b9d004c6Schristos 	ret = get_default_cache(context, dc, &residual);
338b9d004c6Schristos 	if (ret) {
339b9d004c6Schristos 	    dcc_release(context, dc);
340b9d004c6Schristos 	    return ret;
341b9d004c6Schristos 	}
342b9d004c6Schristos 	asprintf(&dc->name, ":%s/%s", dc->dir, residual);
343b9d004c6Schristos 	free(residual);
344b9d004c6Schristos 	if (dc->name == NULL) {
345b9d004c6Schristos 	    dcc_release(context, dc);
346b9d004c6Schristos 	    return krb5_enomem(context);
347b9d004c6Schristos 	}
348b9d004c6Schristos     }
349b9d004c6Schristos 
350b9d004c6Schristos     asprintf(&filename, "FILE%s", dc->name);
351b9d004c6Schristos     if (filename == NULL) {
352b9d004c6Schristos 	dcc_release(context, dc);
353b9d004c6Schristos 	return krb5_enomem(context);
354b9d004c6Schristos     }
355b9d004c6Schristos 
356b9d004c6Schristos     ret = krb5_cc_resolve(context, filename, &dc->fcache);
357b9d004c6Schristos     free(filename);
358b9d004c6Schristos     if (ret) {
359b9d004c6Schristos 	dcc_release(context, dc);
360b9d004c6Schristos 	return ret;
361b9d004c6Schristos     }
362b9d004c6Schristos 
363b9d004c6Schristos 
364b9d004c6Schristos     (*id)->data.data = dc;
365b9d004c6Schristos     (*id)->data.length = sizeof(*dc);
366b9d004c6Schristos     return 0;
367b9d004c6Schristos }
368b9d004c6Schristos 
369b9d004c6Schristos static char *
copy_default_dcc_cache(krb5_context context)370b9d004c6Schristos copy_default_dcc_cache(krb5_context context)
371b9d004c6Schristos {
372b9d004c6Schristos     const char *defname;
373b9d004c6Schristos     krb5_error_code ret;
374b9d004c6Schristos     char *name = NULL;
375b9d004c6Schristos     size_t len;
376b9d004c6Schristos 
377b9d004c6Schristos     len = strlen(krb5_dcc_ops.prefix);
378b9d004c6Schristos 
379b9d004c6Schristos     defname = krb5_cc_default_name(context);
380b9d004c6Schristos     if (defname == NULL ||
381b9d004c6Schristos 	strncmp(defname, krb5_dcc_ops.prefix, len) != 0 ||
382b9d004c6Schristos 	defname[len] != ':')
383b9d004c6Schristos     {
384b9d004c6Schristos 	ret = dcc_get_default_name(context, &name);
385b9d004c6Schristos 	if (ret)
386b9d004c6Schristos 	    return NULL;
387b9d004c6Schristos 
388b9d004c6Schristos 	return name;
389b9d004c6Schristos     } else {
390b9d004c6Schristos 	return strdup(&defname[len + 1]);
391b9d004c6Schristos     }
392b9d004c6Schristos }
393b9d004c6Schristos 
394b9d004c6Schristos 
395b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_gen_new(krb5_context context,krb5_ccache * id)396b9d004c6Schristos dcc_gen_new(krb5_context context, krb5_ccache *id)
397b9d004c6Schristos {
398b9d004c6Schristos     krb5_error_code ret;
399b9d004c6Schristos     char *name = NULL;
400b9d004c6Schristos     krb5_dcache *dc;
401b9d004c6Schristos     int fd;
402b9d004c6Schristos     size_t len;
403b9d004c6Schristos 
404b9d004c6Schristos     name = copy_default_dcc_cache(context);
405b9d004c6Schristos     if (name == NULL) {
406b9d004c6Schristos 	krb5_set_error_message(context, KRB5_CC_FORMAT,
407b9d004c6Schristos 			       N_("Can't generate DIR caches unless its the default type", ""));
408b9d004c6Schristos 	return KRB5_CC_FORMAT;
409b9d004c6Schristos     }
410b9d004c6Schristos 
411b9d004c6Schristos     len = strlen(krb5_dcc_ops.prefix);
412b9d004c6Schristos     if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':')
413b9d004c6Schristos 	++len;
414b9d004c6Schristos     else
415b9d004c6Schristos 	len = 0;
416b9d004c6Schristos 
417b9d004c6Schristos     ret = dcc_resolve(context, id, name + len);
418b9d004c6Schristos     free(name);
419b9d004c6Schristos     name = NULL;
420b9d004c6Schristos     if (ret)
421b9d004c6Schristos 	return ret;
422b9d004c6Schristos 
423b9d004c6Schristos     dc = DCACHE((*id));
424b9d004c6Schristos 
425b9d004c6Schristos     asprintf(&name, ":%s/tktXXXXXX", dc->dir);
426b9d004c6Schristos     if (name == NULL) {
427b9d004c6Schristos 	dcc_close(context, *id);
428b9d004c6Schristos 	return krb5_enomem(context);
429b9d004c6Schristos     }
430b9d004c6Schristos 
431b9d004c6Schristos     fd = mkstemp(&name[1]);
432b9d004c6Schristos     if (fd < 0) {
433b9d004c6Schristos 	dcc_close(context, *id);
434b9d004c6Schristos 	return krb5_enomem(context);
435b9d004c6Schristos     }
436b9d004c6Schristos     close(fd);
437b9d004c6Schristos 
438b9d004c6Schristos     free(dc->name);
439b9d004c6Schristos     dc->name = name;
440b9d004c6Schristos 
441b9d004c6Schristos     return 0;
442b9d004c6Schristos }
443b9d004c6Schristos 
444b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_initialize(krb5_context context,krb5_ccache id,krb5_principal primary_principal)445b9d004c6Schristos dcc_initialize(krb5_context context,
446b9d004c6Schristos 	       krb5_ccache id,
447b9d004c6Schristos 	       krb5_principal primary_principal)
448b9d004c6Schristos {
449b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
450b9d004c6Schristos     return krb5_cc_initialize(context, D2FCACHE(dc), primary_principal);
451b9d004c6Schristos }
452b9d004c6Schristos 
453b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_close(krb5_context context,krb5_ccache id)454b9d004c6Schristos dcc_close(krb5_context context,
455b9d004c6Schristos 	  krb5_ccache id)
456b9d004c6Schristos {
457b9d004c6Schristos     dcc_release(context, DCACHE(id));
458b9d004c6Schristos     return 0;
459b9d004c6Schristos }
460b9d004c6Schristos 
461b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_destroy(krb5_context context,krb5_ccache id)462b9d004c6Schristos dcc_destroy(krb5_context context,
463b9d004c6Schristos 	    krb5_ccache id)
464b9d004c6Schristos {
465b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
466b9d004c6Schristos     krb5_ccache fcache = D2FCACHE(dc);
467b9d004c6Schristos     dc->fcache = NULL;
468b9d004c6Schristos     return krb5_cc_destroy(context, fcache);
469b9d004c6Schristos }
470b9d004c6Schristos 
471b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_store_cred(krb5_context context,krb5_ccache id,krb5_creds * creds)472b9d004c6Schristos dcc_store_cred(krb5_context context,
473b9d004c6Schristos 	       krb5_ccache id,
474b9d004c6Schristos 	       krb5_creds *creds)
475b9d004c6Schristos {
476b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
477b9d004c6Schristos     return krb5_cc_store_cred(context, D2FCACHE(dc), creds);
478b9d004c6Schristos }
479b9d004c6Schristos 
480b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * principal)481b9d004c6Schristos dcc_get_principal(krb5_context context,
482b9d004c6Schristos 		  krb5_ccache id,
483b9d004c6Schristos 		  krb5_principal *principal)
484b9d004c6Schristos {
485b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
486b9d004c6Schristos     return krb5_cc_get_principal(context, D2FCACHE(dc), principal);
487b9d004c6Schristos }
488b9d004c6Schristos 
489b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_first(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)490b9d004c6Schristos dcc_get_first (krb5_context context,
491b9d004c6Schristos 	       krb5_ccache id,
492b9d004c6Schristos 	       krb5_cc_cursor *cursor)
493b9d004c6Schristos {
494b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
495b9d004c6Schristos     return krb5_cc_start_seq_get(context, D2FCACHE(dc), cursor);
496b9d004c6Schristos }
497b9d004c6Schristos 
498b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_next(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)499b9d004c6Schristos dcc_get_next (krb5_context context,
500b9d004c6Schristos 	      krb5_ccache id,
501b9d004c6Schristos 	      krb5_cc_cursor *cursor,
502b9d004c6Schristos 	      krb5_creds *creds)
503b9d004c6Schristos {
504b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
505b9d004c6Schristos     return krb5_cc_next_cred(context, D2FCACHE(dc), cursor, creds);
506b9d004c6Schristos }
507b9d004c6Schristos 
508b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_end_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)509b9d004c6Schristos dcc_end_get (krb5_context context,
510b9d004c6Schristos 	     krb5_ccache id,
511b9d004c6Schristos 	     krb5_cc_cursor *cursor)
512b9d004c6Schristos {
513b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
514b9d004c6Schristos     return krb5_cc_end_seq_get(context, D2FCACHE(dc), cursor);
515b9d004c6Schristos }
516b9d004c6Schristos 
517b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_remove_cred(krb5_context context,krb5_ccache id,krb5_flags which,krb5_creds * cred)518b9d004c6Schristos dcc_remove_cred(krb5_context context,
519b9d004c6Schristos 		 krb5_ccache id,
520b9d004c6Schristos 		 krb5_flags which,
521b9d004c6Schristos 		 krb5_creds *cred)
522b9d004c6Schristos {
523b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
524b9d004c6Schristos     return krb5_cc_remove_cred(context, D2FCACHE(dc), which, cred);
525b9d004c6Schristos }
526b9d004c6Schristos 
527b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)528b9d004c6Schristos dcc_set_flags(krb5_context context,
529b9d004c6Schristos 	      krb5_ccache id,
530b9d004c6Schristos 	      krb5_flags flags)
531b9d004c6Schristos {
532b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
533b9d004c6Schristos     return krb5_cc_set_flags(context, D2FCACHE(dc), flags);
534b9d004c6Schristos }
535b9d004c6Schristos 
536b9d004c6Schristos static int KRB5_CALLCONV
dcc_get_version(krb5_context context,krb5_ccache id)537b9d004c6Schristos dcc_get_version(krb5_context context,
538b9d004c6Schristos 		krb5_ccache id)
539b9d004c6Schristos {
540b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
541b9d004c6Schristos     return krb5_cc_get_version(context, D2FCACHE(dc));
542b9d004c6Schristos }
543b9d004c6Schristos 
544b9d004c6Schristos struct dcache_iter {
545b9d004c6Schristos     int first;
546b9d004c6Schristos     krb5_dcache *dc;
547b9d004c6Schristos };
548b9d004c6Schristos 
549b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_cache_first(krb5_context context,krb5_cc_cursor * cursor)550b9d004c6Schristos dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
551b9d004c6Schristos {
552b9d004c6Schristos     struct dcache_iter *iter;
553b9d004c6Schristos     krb5_error_code ret;
554b9d004c6Schristos     char *name;
555b9d004c6Schristos 
556b9d004c6Schristos     *cursor = NULL;
557b9d004c6Schristos     iter = calloc(1, sizeof(*iter));
558b9d004c6Schristos     if (iter == NULL)
559b9d004c6Schristos 	return krb5_enomem(context);
560b9d004c6Schristos     iter->first = 1;
561b9d004c6Schristos 
562b9d004c6Schristos     name = copy_default_dcc_cache(context);
563b9d004c6Schristos     if (name == NULL) {
564b9d004c6Schristos         free(iter);
565b9d004c6Schristos 	krb5_set_error_message(context, KRB5_CC_FORMAT,
566b9d004c6Schristos 			       N_("Can't generate DIR caches unless its the default type", ""));
567b9d004c6Schristos 	return KRB5_CC_FORMAT;
568b9d004c6Schristos     }
569b9d004c6Schristos 
570b9d004c6Schristos     ret = dcc_resolve(context, NULL, name);
571b9d004c6Schristos     free(name);
572b9d004c6Schristos     if (ret) {
573b9d004c6Schristos         free(iter);
574b9d004c6Schristos         return ret;
575b9d004c6Schristos     }
576b9d004c6Schristos 
577b9d004c6Schristos     /* XXX We need to opendir() here */
578b9d004c6Schristos 
579b9d004c6Schristos     *cursor = iter;
580b9d004c6Schristos     return 0;
581b9d004c6Schristos }
582b9d004c6Schristos 
583b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_cache_next(krb5_context context,krb5_cc_cursor cursor,krb5_ccache * id)584b9d004c6Schristos dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
585b9d004c6Schristos {
586b9d004c6Schristos     struct dcache_iter *iter = cursor;
587b9d004c6Schristos 
588b9d004c6Schristos     if (iter == NULL)
589b9d004c6Schristos         return krb5_einval(context, 2);
590b9d004c6Schristos 
591b9d004c6Schristos     if (!iter->first) {
592b9d004c6Schristos 	krb5_clear_error_message(context);
593b9d004c6Schristos 	return KRB5_CC_END;
594b9d004c6Schristos     }
595b9d004c6Schristos 
596b9d004c6Schristos     /* XXX We need to readdir() here */
597b9d004c6Schristos     iter->first = 0;
598b9d004c6Schristos 
599b9d004c6Schristos     return KRB5_CC_END;
600b9d004c6Schristos }
601b9d004c6Schristos 
602b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_end_cache_get(krb5_context context,krb5_cc_cursor cursor)603b9d004c6Schristos dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
604b9d004c6Schristos {
605b9d004c6Schristos     struct dcache_iter *iter = cursor;
606b9d004c6Schristos 
607b9d004c6Schristos     if (iter == NULL)
608b9d004c6Schristos         return krb5_einval(context, 2);
609b9d004c6Schristos 
610b9d004c6Schristos     /* XXX We need to closedir() here */
611b9d004c6Schristos     if (iter->dc)
612b9d004c6Schristos 	dcc_release(context, iter->dc);
613b9d004c6Schristos     free(iter);
614b9d004c6Schristos     return 0;
615b9d004c6Schristos }
616b9d004c6Schristos 
617b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_move(krb5_context context,krb5_ccache from,krb5_ccache to)618b9d004c6Schristos dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
619b9d004c6Schristos {
620b9d004c6Schristos     krb5_dcache *dcfrom = DCACHE(from);
621b9d004c6Schristos     krb5_dcache *dcto = DCACHE(to);
622b9d004c6Schristos     return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
623b9d004c6Schristos }
624b9d004c6Schristos 
625b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_default_name(krb5_context context,char ** str)626b9d004c6Schristos dcc_get_default_name(krb5_context context, char **str)
627b9d004c6Schristos {
628b9d004c6Schristos     return _krb5_expand_default_cc_name(context,
629b9d004c6Schristos 					KRB5_DEFAULT_CCNAME_DIR,
630b9d004c6Schristos 					str);
631b9d004c6Schristos }
632b9d004c6Schristos 
633b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_set_default(krb5_context context,krb5_ccache id)634b9d004c6Schristos dcc_set_default(krb5_context context, krb5_ccache id)
635b9d004c6Schristos {
636b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
637b9d004c6Schristos     const char *name;
638b9d004c6Schristos 
639b9d004c6Schristos     name = krb5_cc_get_name(context, D2FCACHE(dc));
640b9d004c6Schristos     if (name == NULL)
641b9d004c6Schristos 	return ENOENT;
642b9d004c6Schristos 
643b9d004c6Schristos     return set_default_cache(context, dc, name);
644b9d004c6Schristos }
645b9d004c6Schristos 
646b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_lastchange(krb5_context context,krb5_ccache id,krb5_timestamp * mtime)647b9d004c6Schristos dcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
648b9d004c6Schristos {
649b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
650b9d004c6Schristos     return krb5_cc_last_change_time(context, D2FCACHE(dc), mtime);
651b9d004c6Schristos }
652b9d004c6Schristos 
653b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_set_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat kdc_offset)654b9d004c6Schristos dcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
655b9d004c6Schristos {
656b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
657b9d004c6Schristos     return krb5_cc_set_kdc_offset(context, D2FCACHE(dc), kdc_offset);
658b9d004c6Schristos }
659b9d004c6Schristos 
660b9d004c6Schristos static krb5_error_code KRB5_CALLCONV
dcc_get_kdc_offset(krb5_context context,krb5_ccache id,krb5_deltat * kdc_offset)661b9d004c6Schristos dcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
662b9d004c6Schristos {
663b9d004c6Schristos     krb5_dcache *dc = DCACHE(id);
664b9d004c6Schristos     return krb5_cc_get_kdc_offset(context, D2FCACHE(dc), kdc_offset);
665b9d004c6Schristos }
666b9d004c6Schristos 
667b9d004c6Schristos 
668b9d004c6Schristos /**
669b9d004c6Schristos  * Variable containing the DIR based credential cache implemention.
670b9d004c6Schristos  *
671b9d004c6Schristos  * @ingroup krb5_ccache
672b9d004c6Schristos  */
673b9d004c6Schristos 
674b9d004c6Schristos KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops = {
675b9d004c6Schristos     KRB5_CC_OPS_VERSION,
676b9d004c6Schristos     "DIR",
677b9d004c6Schristos     dcc_get_name,
678b9d004c6Schristos     dcc_resolve,
679b9d004c6Schristos     dcc_gen_new,
680b9d004c6Schristos     dcc_initialize,
681b9d004c6Schristos     dcc_destroy,
682b9d004c6Schristos     dcc_close,
683b9d004c6Schristos     dcc_store_cred,
684b9d004c6Schristos     NULL, /* dcc_retrieve */
685b9d004c6Schristos     dcc_get_principal,
686b9d004c6Schristos     dcc_get_first,
687b9d004c6Schristos     dcc_get_next,
688b9d004c6Schristos     dcc_end_get,
689b9d004c6Schristos     dcc_remove_cred,
690b9d004c6Schristos     dcc_set_flags,
691b9d004c6Schristos     dcc_get_version,
692b9d004c6Schristos     dcc_get_cache_first,
693b9d004c6Schristos     dcc_get_cache_next,
694b9d004c6Schristos     dcc_end_cache_get,
695b9d004c6Schristos     dcc_move,
696b9d004c6Schristos     dcc_get_default_name,
697b9d004c6Schristos     dcc_set_default,
698b9d004c6Schristos     dcc_lastchange,
699b9d004c6Schristos     dcc_set_kdc_offset,
700b9d004c6Schristos     dcc_get_kdc_offset
701b9d004c6Schristos };
702