Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / cccursor.c
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
34 #include <assert.h>
35
36 struct _krb5_cccol_cursor {
37     krb5_cc_typecursor typecursor;
38     const krb5_cc_ops *ops;
39     krb5_cc_ptcursor ptcursor;
40 };
41 /* typedef of krb5_cccol_cursor is in krb5.h */
42
43 krb5_error_code KRB5_CALLCONV
44 krb5_cccol_cursor_new(krb5_context context,
45                       krb5_cccol_cursor *cursor)
46 {
47     krb5_error_code ret = 0;
48     krb5_cccol_cursor n = NULL;
49
50     *cursor = NULL;
51     n = malloc(sizeof(*n));
52     if (n == NULL)
53         return ENOMEM;
54
55     n->typecursor = NULL;
56     n->ptcursor = NULL;
57     n->ops = NULL;
58
59     ret = krb5int_cc_typecursor_new(context, &n->typecursor);
60     if (ret)
61         goto errout;
62
63     do {
64         /* Find first backend with ptcursor functionality. */
65         ret = krb5int_cc_typecursor_next(context, n->typecursor, &n->ops);
66         if (ret || n->ops == NULL)
67             goto errout;
68     } while (n->ops->ptcursor_new == NULL);
69
70     ret = n->ops->ptcursor_new(context, &n->ptcursor);
71     if (ret)
72         goto errout;
73
74 errout:
75     if (ret) {
76         krb5_cccol_cursor_free(context, &n);
77     }
78     *cursor = n;
79     return ret;
80 }
81
82 krb5_error_code KRB5_CALLCONV
83 krb5_cccol_cursor_next(krb5_context context,
84                        krb5_cccol_cursor cursor,
85                        krb5_ccache *ccache_out)
86 {
87     krb5_error_code ret = 0;
88     krb5_ccache ccache;
89
90     *ccache_out = NULL;
91
92     /* Are we out of backends? */
93     if (cursor->ops == NULL)
94         return 0;
95
96     while (1) {
97         ret = cursor->ops->ptcursor_next(context, cursor->ptcursor, &ccache);
98         if (ret)
99             return ret;
100         if (ccache != NULL) {
101             *ccache_out = ccache;
102             return 0;
103         }
104
105         ret = cursor->ops->ptcursor_free(context, &cursor->ptcursor);
106         if (ret)
107             return ret;
108
109         do {
110             /* Find next type with ptcursor functionality. */
111             ret = krb5int_cc_typecursor_next(context, cursor->typecursor,
112                                              &cursor->ops);
113             if (ret)
114                 return ret;
115             if (cursor->ops == NULL)
116                 return 0;
117         } while (cursor->ops->ptcursor_new == NULL);
118
119         ret = cursor->ops->ptcursor_new(context, &cursor->ptcursor);
120         if (ret)
121             return ret;
122     }
123 }
124
125 krb5_error_code KRB5_CALLCONV
126 krb5_cccol_cursor_free(krb5_context context,
127                        krb5_cccol_cursor *cursor)
128 {
129     krb5_cccol_cursor c = *cursor;
130
131     if (c == NULL)
132         return 0;
133
134     if (c->ptcursor != NULL)
135         c->ops->ptcursor_free(context, &c->ptcursor);
136     if (c->typecursor != NULL)
137         krb5int_cc_typecursor_free(context, &c->typecursor);
138     free(c);
139
140     *cursor = NULL;
141     return 0;
142 }
143
144 krb5_error_code KRB5_CALLCONV
145 krb5_cccol_last_change_time(krb5_context context,
146                             krb5_timestamp *change_time)
147 {
148     krb5_error_code ret = 0;
149     krb5_cccol_cursor c = NULL;
150     krb5_ccache ccache = NULL;
151     krb5_timestamp last_time = 0;
152     krb5_timestamp max_change_time = 0;
153
154     *change_time = 0;
155
156     ret = krb5_cccol_cursor_new(context, &c);
157
158     while (!ret) {
159         ret = krb5_cccol_cursor_next(context, c, &ccache);
160         if (ccache) {
161             ret = krb5_cc_last_change_time(context, ccache, &last_time);
162             if (!ret && last_time > max_change_time) {
163                 max_change_time = last_time;
164             }
165             ret = 0;
166         }
167         else {
168             break;
169         }
170     }
171     *change_time = max_change_time;
172     return ret;
173 }
174
175 /*
176  * krb5_cccol_lock and krb5_cccol_unlock are defined in ccbase.c
177  */
178
179 krb5_error_code KRB5_CALLCONV
180 krb5_cc_cache_match(krb5_context context, krb5_principal client,
181                     krb5_ccache *cache_out)
182 {
183     krb5_error_code ret;
184     krb5_cccol_cursor cursor;
185     krb5_ccache cache = NULL;
186     krb5_principal princ;
187     char *name;
188     krb5_boolean eq;
189
190     *cache_out = NULL;
191     ret = krb5_cccol_cursor_new(context, &cursor);
192     if (ret)
193         return ret;
194
195     while ((ret = krb5_cccol_cursor_next(context, cursor, &cache)) == 0 &&
196            cache != NULL) {
197         ret = krb5_cc_get_principal(context, cache, &princ);
198         if (ret == 0) {
199             eq = krb5_principal_compare(context, princ, client);
200             krb5_free_principal(context, princ);
201             if (eq)
202                 break;
203         }
204         krb5_cc_close(context, cache);
205     }
206     krb5_cccol_cursor_free(context, &cursor);
207     if (ret)
208         return ret;
209     if (cache == NULL) {
210         ret = krb5_unparse_name(context, client, &name);
211         if (ret == 0) {
212             k5_setmsg(context, KRB5_CC_NOTFOUND,
213                       _("Can't find client principal %s in cache collection"),
214                       name);
215             krb5_free_unparsed_name(context, name);
216         }
217         ret = KRB5_CC_NOTFOUND;
218     } else
219         *cache_out = cache;
220     return ret;
221 }
222
223 /* Store the error state for code from context into errsave, but only if code
224  * indicates an error and errsave is empty. */
225 static void
226 save_first_error(krb5_context context, krb5_error_code code,
227                  struct errinfo *errsave)
228 {
229     if (code && code != KRB5_CC_END && !errsave->code)
230         k5_save_ctx_error(context, code, errsave);
231 }
232
233 krb5_error_code KRB5_CALLCONV
234 krb5_cccol_have_content(krb5_context context)
235 {
236     krb5_error_code ret;
237     krb5_cccol_cursor col_cursor;
238     krb5_cc_cursor cache_cursor;
239     krb5_ccache cache;
240     krb5_creds creds;
241     krb5_boolean found = FALSE;
242     struct errinfo errsave = EMPTY_ERRINFO;
243     const char *defname;
244
245     ret = krb5_cccol_cursor_new(context, &col_cursor);
246     save_first_error(context, ret, &errsave);
247     if (ret)
248         goto no_entries;
249
250     while (!found) {
251         ret = krb5_cccol_cursor_next(context, col_cursor, &cache);
252         save_first_error(context, ret, &errsave);
253         if (ret || cache == NULL)
254             break;
255
256         ret = krb5_cc_start_seq_get(context, cache, &cache_cursor);
257         save_first_error(context, ret, &errsave);
258         if (ret) {
259             krb5_cc_close(context, cache);
260             continue;
261         }
262         while (!found) {
263             ret = krb5_cc_next_cred(context, cache, &cache_cursor, &creds);
264             save_first_error(context, ret, &errsave);
265             if (ret)
266                 break;
267
268             if (!krb5_is_config_principal(context, creds.server))
269                 found = TRUE;
270             krb5_free_cred_contents(context, &creds);
271         }
272         krb5_cc_end_seq_get(context, cache, &cache_cursor);
273         krb5_cc_close(context, cache);
274     }
275     krb5_cccol_cursor_free(context, &col_cursor);
276     if (found)
277         return 0;
278
279 no_entries:
280     if (errsave.code) {
281         /* Report the first error we encountered. */
282         ret = k5_restore_ctx_error(context, &errsave);
283         k5_wrapmsg(context, ret, KRB5_CC_NOTFOUND,
284                    _("No Kerberos credentials available"));
285     } else {
286         /* Report the default cache name. */
287         defname = krb5_cc_default_name(context);
288         if (defname != NULL) {
289             k5_setmsg(context, KRB5_CC_NOTFOUND,
290                       _("No Kerberos credentials available "
291                         "(default cache: %s)"), defname);
292         }
293     }
294     return KRB5_CC_NOTFOUND;
295 }