xref: /freebsd/crypto/krb5/src/lib/krb5/ccache/cccursor.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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