Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / heimdal / lib / krb5 / cache.c
1 /*
2  * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "krb5_locl.h"
35
36 RCSID("$Id: cache.c,v 1.49 2002/05/29 16:08:23 joda Exp $");
37
38 /*
39  * Add a new ccache type with operations `ops', overwriting any
40  * existing one if `override'.
41  * Return an error code or 0.
42  */
43
44 krb5_error_code
45 krb5_cc_register(krb5_context context, 
46                  const krb5_cc_ops *ops, 
47                  krb5_boolean override)
48 {
49     int i;
50
51     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
52         if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
53             if(!override) {
54                 krb5_set_error_string(context,
55                                       "ccache type %s already exists",
56                                       ops->prefix);
57                 return KRB5_CC_TYPE_EXISTS;
58             }
59             break;
60         }
61     }
62     if(i == context->num_cc_ops) {
63         krb5_cc_ops *o = realloc(context->cc_ops,
64                                  (context->num_cc_ops + 1) *
65                                  sizeof(*context->cc_ops));
66         if(o == NULL) {
67             krb5_set_error_string(context, "malloc: out of memory");
68             return KRB5_CC_NOMEM;
69         }
70         context->num_cc_ops++;
71         context->cc_ops = o;
72         memset(context->cc_ops + i, 0, 
73                (context->num_cc_ops - i) * sizeof(*context->cc_ops));
74     }
75     memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
76     return 0;
77 }
78
79 /*
80  * Allocate memory for a new ccache in `id' with operations `ops'
81  * and name `residual'.
82  * Return 0 or an error code.
83  */
84
85 static krb5_error_code
86 allocate_ccache (krb5_context context,
87                  const krb5_cc_ops *ops,
88                  const char *residual,
89                  krb5_ccache *id)
90 {
91     krb5_error_code ret;
92     krb5_ccache p;
93
94     p = malloc(sizeof(*p));
95     if(p == NULL) {
96         krb5_set_error_string(context, "malloc: out of memory");
97         return KRB5_CC_NOMEM;
98     }
99     p->ops = ops;
100     *id = p;
101     ret = p->ops->resolve(context, id, residual);
102     if(ret)
103         free(p);
104     return ret;
105 }
106
107 /*
108  * Find and allocate a ccache in `id' from the specification in `residual'.
109  * If the ccache name doesn't contain any colon, interpret it as a file name.
110  * Return 0 or an error code.
111  */
112
113 krb5_error_code
114 krb5_cc_resolve(krb5_context context,
115                 const char *name,
116                 krb5_ccache *id)
117 {
118     int i;
119
120     for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
121         size_t prefix_len = strlen(context->cc_ops[i].prefix);
122
123         if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
124            && name[prefix_len] == ':') {
125             return allocate_ccache (context, &context->cc_ops[i],
126                                     name + prefix_len + 1,
127                                     id);
128         }
129     }
130     if (strchr (name, ':') == NULL)
131         return allocate_ccache (context, &krb5_fcc_ops, name, id);
132     else {
133         krb5_set_error_string(context, "unknown ccache type %s", name);
134         return KRB5_CC_UNKNOWN_TYPE;
135     }
136 }
137
138 /*
139  * Generate a new ccache of type `ops' in `id'.
140  * Return 0 or an error code.
141  */
142
143 krb5_error_code
144 krb5_cc_gen_new(krb5_context context,
145                 const krb5_cc_ops *ops,
146                 krb5_ccache *id)
147 {
148     krb5_ccache p;
149
150     p = malloc (sizeof(*p));
151     if (p == NULL) {
152         krb5_set_error_string(context, "malloc: out of memory");
153         return KRB5_CC_NOMEM;
154     }
155     p->ops = ops;
156     *id = p;
157     return p->ops->gen_new(context, id);
158 }
159
160 /*
161  * Return the name of the ccache `id'
162  */
163
164 const char*
165 krb5_cc_get_name(krb5_context context,
166                  krb5_ccache id)
167 {
168     return id->ops->get_name(context, id);
169 }
170
171 /*
172  * Return the type of the ccache `id'.
173  */
174
175 const char*
176 krb5_cc_get_type(krb5_context context,
177                  krb5_ccache id)
178 {
179     return id->ops->prefix;
180 }
181
182 /*
183  * Return a pointer to a static string containing the default ccache name.
184  */
185
186 const char*
187 krb5_cc_default_name(krb5_context context)
188 {
189     static char name[1024];
190     char *p;
191
192     p = getenv("KRB5CCNAME");
193     if(p)
194         strlcpy (name, p, sizeof(name));
195     else
196         snprintf(name,
197                  sizeof(name),
198                  "FILE:/tmp/krb5cc_%u",
199                  (unsigned)getuid());
200     return name;
201 }
202
203 /*
204  * Open the default ccache in `id'.
205  * Return 0 or an error code.
206  */
207
208 krb5_error_code
209 krb5_cc_default(krb5_context context,
210                 krb5_ccache *id)
211 {
212     return krb5_cc_resolve(context, 
213                            krb5_cc_default_name(context), 
214                            id);
215 }
216
217 /*
218  * Create a new ccache in `id' for `primary_principal'.
219  * Return 0 or an error code.
220  */
221
222 krb5_error_code
223 krb5_cc_initialize(krb5_context context,
224                    krb5_ccache id,
225                    krb5_principal primary_principal)
226 {
227     return id->ops->init(context, id, primary_principal);
228 }
229
230
231 /*
232  * Remove the ccache `id'.
233  * Return 0 or an error code.
234  */
235
236 krb5_error_code
237 krb5_cc_destroy(krb5_context context,
238                 krb5_ccache id)
239 {
240     krb5_error_code ret;
241
242     ret = id->ops->destroy(context, id);
243     krb5_cc_close (context, id);
244     return ret;
245 }
246
247 /*
248  * Stop using the ccache `id' and free the related resources.
249  * Return 0 or an error code.
250  */
251
252 krb5_error_code
253 krb5_cc_close(krb5_context context,
254               krb5_ccache id)
255 {
256     krb5_error_code ret;
257     ret = id->ops->close(context, id);
258     free(id);
259     return ret;
260 }
261
262 /*
263  * Store `creds' in the ccache `id'.
264  * Return 0 or an error code.
265  */
266
267 krb5_error_code
268 krb5_cc_store_cred(krb5_context context,
269                    krb5_ccache id,
270                    krb5_creds *creds)
271 {
272     return id->ops->store(context, id, creds);
273 }
274
275 /*
276  * Retrieve the credential identified by `mcreds' (and `whichfields')
277  * from `id' in `creds'.
278  * Return 0 or an error code.
279  */
280
281 krb5_error_code
282 krb5_cc_retrieve_cred(krb5_context context,
283                       krb5_ccache id,
284                       krb5_flags whichfields,
285                       const krb5_creds *mcreds,
286                       krb5_creds *creds)
287 {
288     krb5_error_code ret;
289     krb5_cc_cursor cursor;
290     krb5_cc_start_seq_get(context, id, &cursor);
291     while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
292         if(krb5_compare_creds(context, whichfields, mcreds, creds)){
293             ret = 0;
294             break;
295         }
296         krb5_free_creds_contents (context, creds);
297     }
298     krb5_cc_end_seq_get(context, id, &cursor);
299     return ret;
300 }
301
302 /*
303  * Return the principal of `id' in `principal'.
304  * Return 0 or an error code.
305  */
306
307 krb5_error_code
308 krb5_cc_get_principal(krb5_context context,
309                       krb5_ccache id,
310                       krb5_principal *principal)
311 {
312     return id->ops->get_princ(context, id, principal);
313 }
314
315 /*
316  * Start iterating over `id', `cursor' is initialized to the
317  * beginning.
318  * Return 0 or an error code.
319  */
320
321 krb5_error_code
322 krb5_cc_start_seq_get (krb5_context context,
323                        const krb5_ccache id,
324                        krb5_cc_cursor *cursor)
325 {
326     return id->ops->get_first(context, id, cursor);
327 }
328
329 /*
330  * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
331  * and advance `cursor'.
332  * Return 0 or an error code.
333  */
334
335 krb5_error_code
336 krb5_cc_next_cred (krb5_context context,
337                    const krb5_ccache id,
338                    krb5_cc_cursor *cursor,
339                    krb5_creds *creds)
340 {
341     return id->ops->get_next(context, id, cursor, creds);
342 }
343
344 /*
345  * Destroy the cursor `cursor'.
346  */
347
348 krb5_error_code
349 krb5_cc_end_seq_get (krb5_context context,
350                      const krb5_ccache id,
351                      krb5_cc_cursor *cursor)
352 {
353     return id->ops->end_get(context, id, cursor);
354 }
355
356 /*
357  * Remove the credential identified by `cred', `which' from `id'.
358  */
359
360 krb5_error_code
361 krb5_cc_remove_cred(krb5_context context,
362                     krb5_ccache id,
363                     krb5_flags which,
364                     krb5_creds *cred)
365 {
366     if(id->ops->remove_cred == NULL) {
367         krb5_set_error_string(context,
368                               "ccache %s does not support remove_cred",
369                               id->ops->prefix);
370         return EACCES; /* XXX */
371     }
372     return (*id->ops->remove_cred)(context, id, which, cred);
373 }
374
375 /*
376  * Set the flags of `id' to `flags'.
377  */
378
379 krb5_error_code
380 krb5_cc_set_flags(krb5_context context,
381                   krb5_ccache id,
382                   krb5_flags flags)
383 {
384     return id->ops->set_flags(context, id, flags);
385 }
386                     
387 /*
388  * Copy the contents of `from' to `to'.
389  */
390
391 krb5_error_code
392 krb5_cc_copy_cache(krb5_context context,
393                    const krb5_ccache from,
394                    krb5_ccache to)
395 {
396     krb5_error_code ret;
397     krb5_cc_cursor cursor;
398     krb5_creds cred;
399     krb5_principal princ;
400
401     ret = krb5_cc_get_principal(context, from, &princ);
402     if(ret)
403         return ret;
404     ret = krb5_cc_initialize(context, to, princ);
405     if(ret){
406         krb5_free_principal(context, princ);
407         return ret;
408     }
409     ret = krb5_cc_start_seq_get(context, from, &cursor);
410     if(ret){
411         krb5_free_principal(context, princ);
412         return ret;
413     }
414     while(ret == 0 && krb5_cc_next_cred(context, from, &cursor, &cred) == 0){
415         ret = krb5_cc_store_cred(context, to, &cred);
416         krb5_free_creds_contents (context, &cred);
417     }
418     krb5_cc_end_seq_get(context, from, &cursor);
419     krb5_free_principal(context, princ);
420     return ret;
421 }
422
423 /*
424  * Return the version of `id'.
425  */
426
427 krb5_error_code
428 krb5_cc_get_version(krb5_context context,
429                     const krb5_ccache id)
430 {
431     if(id->ops->get_version)
432         return id->ops->get_version(context, id);
433     else
434         return 0;
435 }