Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / ccfns.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccfns.c - Dispatch methods for credentials cache code.*/
3 /*
4  * Copyright 2000, 2007, 2008  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 #include "k5-int.h"
28 #include "cc-int.h"
29 #include "../krb/int-proto.h"
30
31 const char * KRB5_CALLCONV
32 krb5_cc_get_name(krb5_context context, krb5_ccache cache)
33 {
34     return cache->ops->get_name(context, cache);
35 }
36
37 krb5_error_code KRB5_CALLCONV
38 krb5_cc_get_full_name(krb5_context context, krb5_ccache cache,
39                       char **fullname_out)
40 {
41     char *name;
42
43     *fullname_out = NULL;
44     if (asprintf(&name, "%s:%s", cache->ops->prefix,
45                  cache->ops->get_name(context, cache)) < 0)
46         return ENOMEM;
47     *fullname_out = name;
48     return 0;
49 }
50
51 krb5_error_code KRB5_CALLCONV
52 krb5_cc_gen_new(krb5_context context, krb5_ccache *cache)
53 {
54     TRACE_CC_GEN_NEW(context, cache);
55     return (*cache)->ops->gen_new(context, cache);
56 }
57
58 krb5_error_code KRB5_CALLCONV
59 krb5_cc_initialize(krb5_context context, krb5_ccache cache,
60                    krb5_principal principal)
61 {
62     TRACE_CC_INIT(context, cache, principal);
63     return cache->ops->init(context, cache, principal);
64 }
65
66 krb5_error_code KRB5_CALLCONV
67 krb5_cc_destroy(krb5_context context, krb5_ccache cache)
68 {
69     TRACE_CC_DESTROY(context, cache);
70     return cache->ops->destroy(context, cache);
71 }
72
73 krb5_error_code KRB5_CALLCONV
74 krb5_cc_close(krb5_context context, krb5_ccache cache)
75 {
76     return cache->ops->close(context, cache);
77 }
78
79 krb5_error_code KRB5_CALLCONV
80 krb5_cc_store_cred(krb5_context context, krb5_ccache cache,
81                    krb5_creds *creds)
82 {
83     krb5_error_code ret;
84     krb5_ticket *tkt;
85     krb5_principal s1, s2;
86
87     TRACE_CC_STORE(context, cache, creds);
88     ret = cache->ops->store(context, cache, creds);
89     if (ret) return ret;
90
91     /*
92      * If creds->server and the server in the decoded ticket differ,
93      * store both principals.
94      */
95     s1 = creds->server;
96     ret = decode_krb5_ticket(&creds->ticket, &tkt);
97     /* Bail out on errors in case someone is storing a non-ticket. */
98     if (ret) return 0;
99     s2 = tkt->server;
100     if (!krb5_principal_compare(context, s1, s2)) {
101         creds->server = s2;
102         TRACE_CC_STORE_TKT(context, cache, creds);
103         /* remove any dups */
104         krb5_cc_remove_cred(context, cache, KRB5_TC_MATCH_AUTHDATA, creds);
105         ret = cache->ops->store(context, cache, creds);
106         creds->server = s1;
107     }
108     krb5_free_ticket(context, tkt);
109     return ret;
110 }
111
112 krb5_error_code KRB5_CALLCONV
113 krb5_cc_retrieve_cred(krb5_context context, krb5_ccache cache,
114                       krb5_flags flags, krb5_creds *mcreds,
115                       krb5_creds *creds)
116 {
117     krb5_error_code ret;
118     krb5_data tmprealm;
119
120     ret = cache->ops->retrieve(context, cache, flags, mcreds, creds);
121     TRACE_CC_RETRIEVE(context, cache, mcreds, ret);
122     if (ret != KRB5_CC_NOTFOUND)
123         return ret;
124     if (!krb5_is_referral_realm(&mcreds->server->realm))
125         return ret;
126
127     /*
128      * Retry using client's realm if service has referral realm.
129      */
130     tmprealm = mcreds->server->realm;
131     mcreds->server->realm = mcreds->client->realm;
132     ret = cache->ops->retrieve(context, cache, flags, mcreds, creds);
133     TRACE_CC_RETRIEVE_REF(context, cache, mcreds, ret);
134     mcreds->server->realm = tmprealm;
135     return ret;
136 }
137
138 krb5_error_code KRB5_CALLCONV
139 krb5_cc_get_principal(krb5_context context, krb5_ccache cache,
140                       krb5_principal *principal)
141 {
142     return cache->ops->get_princ(context, cache, principal);
143 }
144
145 krb5_error_code KRB5_CALLCONV
146 krb5_cc_start_seq_get(krb5_context context, krb5_ccache cache,
147                       krb5_cc_cursor *cursor)
148 {
149     return cache->ops->get_first(context, cache, cursor);
150 }
151
152 krb5_error_code KRB5_CALLCONV
153 krb5_cc_next_cred(krb5_context context, krb5_ccache cache,
154                   krb5_cc_cursor *cursor, krb5_creds *creds)
155 {
156     return cache->ops->get_next(context, cache, cursor, creds);
157 }
158
159 krb5_error_code KRB5_CALLCONV
160 krb5_cc_end_seq_get(krb5_context context, krb5_ccache cache,
161                     krb5_cc_cursor *cursor)
162 {
163     return cache->ops->end_get(context, cache, cursor);
164 }
165
166 krb5_error_code KRB5_CALLCONV
167 krb5_cc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
168                     krb5_creds *creds)
169 {
170     TRACE_CC_REMOVE(context, cache, creds);
171     return cache->ops->remove_cred(context, cache, flags, creds);
172 }
173
174 krb5_error_code KRB5_CALLCONV
175 krb5_cc_set_flags(krb5_context context, krb5_ccache cache, krb5_flags flags)
176 {
177     return cache->ops->set_flags(context, cache, flags);
178 }
179
180 krb5_error_code KRB5_CALLCONV
181 krb5_cc_get_flags(krb5_context context, krb5_ccache cache, krb5_flags *flags)
182 {
183     return cache->ops->get_flags(context, cache, flags);
184 }
185
186 const char * KRB5_CALLCONV
187 krb5_cc_get_type(krb5_context context, krb5_ccache cache)
188 {
189     return cache->ops->prefix;
190 }
191
192 krb5_error_code KRB5_CALLCONV
193 krb5_cc_last_change_time(krb5_context context, krb5_ccache ccache,
194                          krb5_timestamp *change_time)
195 {
196     return ccache->ops->lastchange(context, ccache, change_time);
197 }
198
199 krb5_error_code KRB5_CALLCONV
200 krb5_cc_lock(krb5_context context, krb5_ccache ccache)
201 {
202     return ccache->ops->lock(context, ccache);
203 }
204
205 krb5_error_code KRB5_CALLCONV
206 krb5_cc_unlock(krb5_context context, krb5_ccache ccache)
207 {
208     return ccache->ops->unlock(context, ccache);
209 }
210
211 static const char conf_realm[] = "X-CACHECONF:";
212 static const char conf_name[] = "krb5_ccache_conf_data";
213
214 krb5_error_code
215 k5_build_conf_principals(krb5_context context, krb5_ccache id,
216                          krb5_const_principal principal,
217                          const char *name, krb5_creds *cred)
218 {
219     krb5_principal client;
220     krb5_error_code ret;
221     char *pname = NULL;
222
223     memset(cred, 0, sizeof(*cred));
224
225     ret = krb5_cc_get_principal(context, id, &client);
226     if (ret)
227         return ret;
228
229     if (principal) {
230         ret = krb5_unparse_name(context, principal, &pname);
231         if (ret)
232             return ret;
233     }
234
235     ret = krb5_build_principal(context, &cred->server,
236                                sizeof(conf_realm) - 1, conf_realm,
237                                conf_name, name, pname, (char *)NULL);
238     krb5_free_unparsed_name(context, pname);
239     if (ret) {
240         krb5_free_principal(context, client);
241         return ret;
242     }
243     ret = krb5_copy_principal(context, client, &cred->client);
244     krb5_free_principal(context, client);
245     return ret;
246 }
247
248 krb5_boolean KRB5_CALLCONV
249 krb5_is_config_principal(krb5_context context,
250                          krb5_const_principal principal)
251 {
252     const krb5_data *realm = &principal->realm;
253
254     if (realm->length != sizeof(conf_realm) - 1 ||
255         memcmp(realm->data, conf_realm, sizeof(conf_realm) - 1) != 0)
256         return FALSE;
257
258     if (principal->length == 0 ||
259         principal->data[0].length != (sizeof(conf_name) - 1) ||
260         memcmp(principal->data[0].data, conf_name, sizeof(conf_name) - 1) != 0)
261         return FALSE;
262
263     return TRUE;
264 }
265
266 krb5_error_code KRB5_CALLCONV
267 krb5_cc_set_config(krb5_context context, krb5_ccache id,
268                    krb5_const_principal principal,
269                    const char *key, krb5_data *data)
270 {
271     krb5_error_code ret;
272     krb5_creds cred;
273     memset(&cred, 0, sizeof(cred));
274
275     TRACE_CC_SET_CONFIG(context, id, principal, key, data);
276
277     ret = k5_build_conf_principals(context, id, principal, key, &cred);
278     if (ret)
279         goto out;
280
281     if (data == NULL) {
282         ret = krb5_cc_remove_cred(context, id, 0, &cred);
283     } else {
284         ret = krb5int_copy_data_contents(context, data, &cred.ticket);
285         if (ret)
286             goto out;
287         ret = krb5_cc_store_cred(context, id, &cred);
288     }
289 out:
290     krb5_free_cred_contents(context, &cred);
291     return ret;
292 }
293
294 krb5_error_code KRB5_CALLCONV
295 krb5_cc_get_config(krb5_context context, krb5_ccache id,
296                    krb5_const_principal principal,
297                    const char *key, krb5_data *data)
298 {
299     krb5_creds mcred, cred;
300     krb5_error_code ret;
301
302     memset(&cred, 0, sizeof(cred));
303     memset(data, 0, sizeof(*data));
304
305     ret = k5_build_conf_principals(context, id, principal, key, &mcred);
306     if (ret)
307         goto out;
308
309     ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
310     if (ret)
311         goto out;
312
313     ret = krb5int_copy_data_contents(context, &cred.ticket, data);
314     if (ret)
315         goto out;
316
317     TRACE_CC_GET_CONFIG(context, id, principal, key, data);
318
319 out:
320     krb5_free_cred_contents(context, &cred);
321     krb5_free_cred_contents(context, &mcred);
322     return ret;
323 }
324
325 krb5_error_code KRB5_CALLCONV
326 krb5_cc_switch(krb5_context context, krb5_ccache cache)
327 {
328     if (cache->ops->switch_to == NULL)
329         return 0;
330     return cache->ops->switch_to(context, cache);
331 }