1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cccursor.c */
3 /*
4 * Copyright 2006, 2007 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 /*
28 * cursor for sequential traversal of ccaches
29 */
30
31 #include "cc-int.h"
32 #include "../krb/int-proto.h"
33 #include "../os/os-proto.h"
34
35 #include <assert.h>
36
37 struct _krb5_cccol_cursor {
38 krb5_cc_typecursor typecursor;
39 const krb5_cc_ops *ops;
40 krb5_cc_ptcursor ptcursor;
41 };
42 /* typedef of krb5_cccol_cursor is in krb5.h */
43
44 krb5_error_code KRB5_CALLCONV
krb5_cccol_cursor_new(krb5_context context,krb5_cccol_cursor * cursor)45 krb5_cccol_cursor_new(krb5_context context,
46 krb5_cccol_cursor *cursor)
47 {
48 krb5_error_code ret = 0;
49 krb5_cccol_cursor n = NULL;
50
51 *cursor = NULL;
52 n = malloc(sizeof(*n));
53 if (n == NULL)
54 return ENOMEM;
55
56 n->typecursor = NULL;
57 n->ptcursor = NULL;
58 n->ops = NULL;
59
60 ret = krb5int_cc_typecursor_new(context, &n->typecursor);
61 if (ret)
62 goto errout;
63
64 do {
65 /* Find first backend with ptcursor functionality. */
66 ret = krb5int_cc_typecursor_next(context, n->typecursor, &n->ops);
67 if (ret || n->ops == NULL)
68 goto errout;
69 } while (n->ops->ptcursor_new == NULL);
70
71 ret = n->ops->ptcursor_new(context, &n->ptcursor);
72 if (ret)
73 goto errout;
74
75 errout:
76 if (ret) {
77 krb5_cccol_cursor_free(context, &n);
78 }
79 *cursor = n;
80 return ret;
81 }
82
83 krb5_error_code KRB5_CALLCONV
krb5_cccol_cursor_next(krb5_context context,krb5_cccol_cursor cursor,krb5_ccache * ccache_out)84 krb5_cccol_cursor_next(krb5_context context,
85 krb5_cccol_cursor cursor,
86 krb5_ccache *ccache_out)
87 {
88 krb5_error_code ret = 0;
89 krb5_ccache ccache;
90
91 *ccache_out = NULL;
92
93 /* Are we out of backends? */
94 if (cursor->ops == NULL)
95 return 0;
96
97 while (1) {
98 ret = cursor->ops->ptcursor_next(context, cursor->ptcursor, &ccache);
99 if (ret)
100 return ret;
101 if (ccache != NULL) {
102 *ccache_out = ccache;
103 return 0;
104 }
105
106 ret = cursor->ops->ptcursor_free(context, &cursor->ptcursor);
107 if (ret)
108 return ret;
109
110 do {
111 /* Find next type with ptcursor functionality. */
112 ret = krb5int_cc_typecursor_next(context, cursor->typecursor,
113 &cursor->ops);
114 if (ret)
115 return ret;
116 if (cursor->ops == NULL)
117 return 0;
118 } while (cursor->ops->ptcursor_new == NULL);
119
120 ret = cursor->ops->ptcursor_new(context, &cursor->ptcursor);
121 if (ret)
122 return ret;
123 }
124 }
125
126 krb5_error_code KRB5_CALLCONV
krb5_cccol_cursor_free(krb5_context context,krb5_cccol_cursor * cursor)127 krb5_cccol_cursor_free(krb5_context context,
128 krb5_cccol_cursor *cursor)
129 {
130 krb5_cccol_cursor c = *cursor;
131
132 if (c == NULL)
133 return 0;
134
135 if (c->ptcursor != NULL)
136 c->ops->ptcursor_free(context, &c->ptcursor);
137 if (c->typecursor != NULL)
138 krb5int_cc_typecursor_free(context, &c->typecursor);
139 free(c);
140
141 *cursor = NULL;
142 return 0;
143 }
144
145 static krb5_error_code
match_caches(krb5_context context,krb5_const_principal client,krb5_ccache * cache_out)146 match_caches(krb5_context context, krb5_const_principal client,
147 krb5_ccache *cache_out)
148 {
149 krb5_error_code ret;
150 krb5_cccol_cursor cursor;
151 krb5_ccache cache = NULL;
152 krb5_principal princ;
153 krb5_boolean eq;
154
155 *cache_out = NULL;
156
157 ret = krb5_cccol_cursor_new(context, &cursor);
158 if (ret)
159 return ret;
160
161 while ((ret = krb5_cccol_cursor_next(context, cursor, &cache)) == 0 &&
162 cache != NULL) {
163 ret = krb5_cc_get_principal(context, cache, &princ);
164 if (ret == 0) {
165 eq = krb5_principal_compare(context, princ, client);
166 krb5_free_principal(context, princ);
167 if (eq)
168 break;
169 }
170 krb5_cc_close(context, cache);
171 }
172 krb5_cccol_cursor_free(context, &cursor);
173
174 if (ret)
175 return ret;
176 if (cache == NULL)
177 return KRB5_CC_NOTFOUND;
178
179 *cache_out = cache;
180 return 0;
181 }
182
183 krb5_error_code KRB5_CALLCONV
krb5_cc_cache_match(krb5_context context,krb5_principal client,krb5_ccache * cache_out)184 krb5_cc_cache_match(krb5_context context, krb5_principal client,
185 krb5_ccache *cache_out)
186 {
187 krb5_error_code ret;
188 struct canonprinc iter = { client, .subst_defrealm = TRUE };
189 krb5_const_principal canonprinc = NULL;
190 krb5_ccache cache = NULL;
191 char *name;
192
193 *cache_out = NULL;
194
195 while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
196 canonprinc != NULL) {
197 ret = match_caches(context, canonprinc, &cache);
198 if (ret != KRB5_CC_NOTFOUND)
199 break;
200 }
201 free_canonprinc(&iter);
202
203 if (ret == 0 && canonprinc == NULL) {
204 ret = KRB5_CC_NOTFOUND;
205 if (krb5_unparse_name(context, client, &name) == 0) {
206 k5_setmsg(context, ret,
207 _("Can't find client principal %s in cache collection"),
208 name);
209 krb5_free_unparsed_name(context, name);
210 }
211 }
212
213 TRACE_CC_CACHE_MATCH(context, client, ret);
214 if (ret)
215 return ret;
216
217 *cache_out = cache;
218 return 0;
219 }
220
221 /* Store the error state for code from context into errsave, but only if code
222 * indicates an error and errsave is empty. */
223 static void
save_first_error(krb5_context context,krb5_error_code code,struct errinfo * errsave)224 save_first_error(krb5_context context, krb5_error_code code,
225 struct errinfo *errsave)
226 {
227 if (code && code != KRB5_FCC_NOFILE && !errsave->code)
228 k5_save_ctx_error(context, code, errsave);
229 }
230
231 krb5_error_code KRB5_CALLCONV
krb5_cccol_have_content(krb5_context context)232 krb5_cccol_have_content(krb5_context context)
233 {
234 krb5_error_code ret;
235 krb5_cccol_cursor col_cursor;
236 krb5_ccache cache;
237 krb5_principal princ;
238 krb5_boolean found = FALSE;
239 struct errinfo errsave = EMPTY_ERRINFO;
240 const char *defname;
241
242 ret = krb5_cccol_cursor_new(context, &col_cursor);
243 save_first_error(context, ret, &errsave);
244 if (ret)
245 goto no_entries;
246
247 while (!found) {
248 ret = krb5_cccol_cursor_next(context, col_cursor, &cache);
249 save_first_error(context, ret, &errsave);
250 if (ret || cache == NULL)
251 break;
252 princ = NULL;
253 ret = krb5_cc_get_principal(context, cache, &princ);
254 save_first_error(context, ret, &errsave);
255 if (!ret)
256 found = TRUE;
257 krb5_free_principal(context, princ);
258 krb5_cc_close(context, cache);
259 }
260 krb5_cccol_cursor_free(context, &col_cursor);
261 if (found) {
262 k5_clear_error(&errsave);
263 return 0;
264 }
265
266 no_entries:
267 if (errsave.code) {
268 /* Report the first error we encountered. */
269 ret = k5_restore_ctx_error(context, &errsave);
270 k5_wrapmsg(context, ret, KRB5_CC_NOTFOUND,
271 _("No Kerberos credentials available"));
272 } else {
273 /* Report the default cache name. */
274 defname = krb5_cc_default_name(context);
275 if (defname != NULL) {
276 k5_setmsg(context, KRB5_CC_NOTFOUND,
277 _("No Kerberos credentials available "
278 "(default cache: %s)"), defname);
279 }
280 }
281 return KRB5_CC_NOTFOUND;
282 }
283