155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
36178825SdfrRCSID("$Id: cache.c 22127 2007-12-04 00:54:37Z lha $");
3755682Smarkm
38178825Sdfr/**
3955682Smarkm * Add a new ccache type with operations `ops', overwriting any
4055682Smarkm * existing one if `override'.
41178825Sdfr *
42178825Sdfr * @param context a Keberos context
43178825Sdfr * @param ops type of plugin symbol
44178825Sdfr * @param override flag to select if the registration is to overide
45178825Sdfr * an existing ops with the same name.
46178825Sdfr *
47178825Sdfr * @return Return an error code or 0.
48178825Sdfr *
49178825Sdfr * @ingroup krb5_ccache
5055682Smarkm */
5155682Smarkm
52178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
5355682Smarkmkrb5_cc_register(krb5_context context,
5455682Smarkm		 const krb5_cc_ops *ops,
5555682Smarkm		 krb5_boolean override)
5655682Smarkm{
5755682Smarkm    int i;
5855682Smarkm
5955682Smarkm    for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
6055682Smarkm	if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
61102644Snectar	    if(!override) {
6278527Sassar		krb5_set_error_string(context,
6378527Sassar				      "ccache type %s already exists",
6478527Sassar				      ops->prefix);
6555682Smarkm		return KRB5_CC_TYPE_EXISTS;
6678527Sassar	    }
67102644Snectar	    break;
6855682Smarkm	}
6955682Smarkm    }
7055682Smarkm    if(i == context->num_cc_ops) {
7155682Smarkm	krb5_cc_ops *o = realloc(context->cc_ops,
7255682Smarkm				 (context->num_cc_ops + 1) *
7355682Smarkm				 sizeof(*context->cc_ops));
7478527Sassar	if(o == NULL) {
7578527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
7655682Smarkm	    return KRB5_CC_NOMEM;
7778527Sassar	}
7855682Smarkm	context->num_cc_ops++;
7955682Smarkm	context->cc_ops = o;
8055682Smarkm	memset(context->cc_ops + i, 0,
8155682Smarkm	       (context->num_cc_ops - i) * sizeof(*context->cc_ops));
8255682Smarkm    }
8355682Smarkm    memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
8455682Smarkm    return 0;
8555682Smarkm}
8655682Smarkm
8755682Smarkm/*
88178825Sdfr * Allocate the memory for a `id' and the that function table to
89178825Sdfr * `ops'. Returns 0 or and error code.
90178825Sdfr */
91178825Sdfr
92178825Sdfrkrb5_error_code
93178825Sdfr_krb5_cc_allocate(krb5_context context,
94178825Sdfr		  const krb5_cc_ops *ops,
95178825Sdfr		  krb5_ccache *id)
96178825Sdfr{
97178825Sdfr    krb5_ccache p;
98178825Sdfr
99178825Sdfr    p = malloc (sizeof(*p));
100178825Sdfr    if(p == NULL) {
101178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
102178825Sdfr	return KRB5_CC_NOMEM;
103178825Sdfr    }
104178825Sdfr    p->ops = ops;
105178825Sdfr    *id = p;
106178825Sdfr
107178825Sdfr    return 0;
108178825Sdfr}
109178825Sdfr
110178825Sdfr/*
11155682Smarkm * Allocate memory for a new ccache in `id' with operations `ops'
112178825Sdfr * and name `residual'. Return 0 or an error code.
11355682Smarkm */
11455682Smarkm
11555682Smarkmstatic krb5_error_code
11655682Smarkmallocate_ccache (krb5_context context,
11755682Smarkm		 const krb5_cc_ops *ops,
11855682Smarkm		 const char *residual,
11955682Smarkm		 krb5_ccache *id)
12055682Smarkm{
12155682Smarkm    krb5_error_code ret;
12255682Smarkm
123178825Sdfr    ret = _krb5_cc_allocate(context, ops, id);
124178825Sdfr    if (ret)
125178825Sdfr	return ret;
126178825Sdfr    ret = (*id)->ops->resolve(context, id, residual);
12755682Smarkm    if(ret)
128178825Sdfr	free(*id);
12955682Smarkm    return ret;
13055682Smarkm}
13155682Smarkm
132178825Sdfr/**
13355682Smarkm * Find and allocate a ccache in `id' from the specification in `residual'.
13455682Smarkm * If the ccache name doesn't contain any colon, interpret it as a file name.
135178825Sdfr *
136178825Sdfr * @param context a Keberos context.
137178825Sdfr * @param name string name of a credential cache.
138178825Sdfr * @param id return pointer to a found credential cache.
139178825Sdfr *
140178825Sdfr * @return Return 0 or an error code. In case of an error, id is set
141178825Sdfr * to NULL.
142178825Sdfr *
143178825Sdfr * @ingroup krb5_ccache
14455682Smarkm */
14555682Smarkm
146178825Sdfr
147178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
14855682Smarkmkrb5_cc_resolve(krb5_context context,
14955682Smarkm		const char *name,
15055682Smarkm		krb5_ccache *id)
15155682Smarkm{
15255682Smarkm    int i;
15355682Smarkm
154178825Sdfr    *id = NULL;
155178825Sdfr
15655682Smarkm    for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
15755682Smarkm	size_t prefix_len = strlen(context->cc_ops[i].prefix);
15855682Smarkm
15955682Smarkm	if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
16055682Smarkm	   && name[prefix_len] == ':') {
16155682Smarkm	    return allocate_ccache (context, &context->cc_ops[i],
16255682Smarkm				    name + prefix_len + 1,
16355682Smarkm				    id);
16455682Smarkm	}
16555682Smarkm    }
16655682Smarkm    if (strchr (name, ':') == NULL)
16755682Smarkm	return allocate_ccache (context, &krb5_fcc_ops, name, id);
16878527Sassar    else {
16978527Sassar	krb5_set_error_string(context, "unknown ccache type %s", name);
17055682Smarkm	return KRB5_CC_UNKNOWN_TYPE;
17178527Sassar    }
17255682Smarkm}
17355682Smarkm
174178825Sdfr/**
17555682Smarkm * Generate a new ccache of type `ops' in `id'.
176178825Sdfr *
177178825Sdfr * @return Return 0 or an error code.
178178825Sdfr *
179178825Sdfr * @ingroup krb5_ccache
18055682Smarkm */
18155682Smarkm
182178825Sdfr
183178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
18455682Smarkmkrb5_cc_gen_new(krb5_context context,
18555682Smarkm		const krb5_cc_ops *ops,
18655682Smarkm		krb5_ccache *id)
18755682Smarkm{
188178825Sdfr    return krb5_cc_new_unique(context, ops->prefix, NULL, id);
189178825Sdfr}
19055682Smarkm
191178825Sdfr/**
192178825Sdfr * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
193178825Sdfr * the library chooses the default credential cache type. The supplied
194178825Sdfr * `hint' (that can be NULL) is a string that the credential cache
195178825Sdfr * type can use to base the name of the credential on, this is to make
196178825Sdfr * it easier for the user to differentiate the credentials.
197178825Sdfr *
198178825Sdfr * @return Returns 0 or an error code.
199178825Sdfr *
200178825Sdfr * @ingroup krb5_ccache
201178825Sdfr */
202178825Sdfr
203178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
204178825Sdfrkrb5_cc_new_unique(krb5_context context, const char *type,
205178825Sdfr		   const char *hint, krb5_ccache *id)
206178825Sdfr{
207178825Sdfr    const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
208178825Sdfr    krb5_error_code ret;
209178825Sdfr
210178825Sdfr    if (type) {
211178825Sdfr	ops = krb5_cc_get_prefix_ops(context, type);
212178825Sdfr	if (ops == NULL) {
213178825Sdfr	    krb5_set_error_string(context,
214178825Sdfr				  "Credential cache type %s is unknown", type);
215178825Sdfr	    return KRB5_CC_UNKNOWN_TYPE;
216178825Sdfr	}
21778527Sassar    }
218178825Sdfr
219178825Sdfr    ret = _krb5_cc_allocate(context, ops, id);
220178825Sdfr    if (ret)
221178825Sdfr	return ret;
222178825Sdfr    return (*id)->ops->gen_new(context, id);
22355682Smarkm}
22455682Smarkm
225178825Sdfr/**
22655682Smarkm * Return the name of the ccache `id'
227178825Sdfr *
228178825Sdfr * @ingroup krb5_ccache
22955682Smarkm */
23055682Smarkm
231178825Sdfr
232178825Sdfrconst char* KRB5_LIB_FUNCTION
23355682Smarkmkrb5_cc_get_name(krb5_context context,
23455682Smarkm		 krb5_ccache id)
23555682Smarkm{
23655682Smarkm    return id->ops->get_name(context, id);
23755682Smarkm}
23855682Smarkm
239178825Sdfr/**
24055682Smarkm * Return the type of the ccache `id'.
241178825Sdfr *
242178825Sdfr * @ingroup krb5_ccache
24355682Smarkm */
24455682Smarkm
245178825Sdfr
246178825Sdfrconst char* KRB5_LIB_FUNCTION
24755682Smarkmkrb5_cc_get_type(krb5_context context,
24855682Smarkm		 krb5_ccache id)
24955682Smarkm{
25055682Smarkm    return id->ops->prefix;
25155682Smarkm}
25255682Smarkm
253178825Sdfr/**
254178825Sdfr * Return the complete resolvable name the ccache `id' in `str�.
255178825Sdfr * `str` should be freed with free(3).
256178825Sdfr * Returns 0 or an error (and then *str is set to NULL).
257178825Sdfr *
258178825Sdfr * @ingroup krb5_ccache
259178825Sdfr */
260178825Sdfr
261178825Sdfr
262178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
263178825Sdfrkrb5_cc_get_full_name(krb5_context context,
264178825Sdfr		      krb5_ccache id,
265178825Sdfr		      char **str)
266178825Sdfr{
267178825Sdfr    const char *type, *name;
268178825Sdfr
269178825Sdfr    *str = NULL;
270178825Sdfr
271178825Sdfr    type = krb5_cc_get_type(context, id);
272178825Sdfr    if (type == NULL) {
273178825Sdfr	krb5_set_error_string(context, "cache have no name of type");
274178825Sdfr	return KRB5_CC_UNKNOWN_TYPE;
275178825Sdfr    }
276178825Sdfr
277178825Sdfr    name = krb5_cc_get_name(context, id);
278178825Sdfr    if (name == NULL) {
279178825Sdfr	krb5_set_error_string(context, "cache of type %s have no name", type);
280178825Sdfr	return KRB5_CC_BADNAME;
281178825Sdfr    }
282178825Sdfr
283178825Sdfr    if (asprintf(str, "%s:%s", type, name) == -1) {
284178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
285178825Sdfr	*str = NULL;
286178825Sdfr	return ENOMEM;
287178825Sdfr    }
288178825Sdfr    return 0;
289178825Sdfr}
290178825Sdfr
291178825Sdfr/**
292120945Snectar * Return krb5_cc_ops of a the ccache `id'.
293178825Sdfr *
294178825Sdfr * @ingroup krb5_ccache
29555682Smarkm */
29655682Smarkm
297178825Sdfr
298120945Snectarconst krb5_cc_ops *
299120945Snectarkrb5_cc_get_ops(krb5_context context, krb5_ccache id)
300120945Snectar{
301120945Snectar    return id->ops;
302120945Snectar}
303120945Snectar
304120945Snectar/*
305178825Sdfr * Expand variables in `str' into `res'
306178825Sdfr */
307178825Sdfr
308178825Sdfrkrb5_error_code
309178825Sdfr_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
310178825Sdfr{
311178825Sdfr    size_t tlen, len = 0;
312178825Sdfr    char *tmp, *tmp2, *append;
313178825Sdfr
314178825Sdfr    *res = NULL;
315178825Sdfr
316178825Sdfr    while (str && *str) {
317178825Sdfr	tmp = strstr(str, "%{");
318178825Sdfr	if (tmp && tmp != str) {
319178825Sdfr	    append = malloc((tmp - str) + 1);
320178825Sdfr	    if (append) {
321178825Sdfr		memcpy(append, str, tmp - str);
322178825Sdfr		append[tmp - str] = '\0';
323178825Sdfr	    }
324178825Sdfr	    str = tmp;
325178825Sdfr	} else if (tmp) {
326178825Sdfr	    tmp2 = strchr(tmp, '}');
327178825Sdfr	    if (tmp2 == NULL) {
328178825Sdfr		free(*res);
329178825Sdfr		*res = NULL;
330178825Sdfr		krb5_set_error_string(context, "variable missing }");
331178825Sdfr		return KRB5_CONFIG_BADFORMAT;
332178825Sdfr	    }
333178825Sdfr	    if (strncasecmp(tmp, "%{uid}", 6) == 0)
334178825Sdfr		asprintf(&append, "%u", (unsigned)getuid());
335178825Sdfr	    else if (strncasecmp(tmp, "%{null}", 7) == 0)
336178825Sdfr		append = strdup("");
337178825Sdfr	    else {
338178825Sdfr		free(*res);
339178825Sdfr		*res = NULL;
340178825Sdfr		krb5_set_error_string(context,
341178825Sdfr				      "expand default cache unknown "
342178825Sdfr				      "variable \"%.*s\"",
343178825Sdfr				      (int)(tmp2 - tmp) - 2, tmp + 2);
344178825Sdfr		return KRB5_CONFIG_BADFORMAT;
345178825Sdfr	    }
346178825Sdfr	    str = tmp2 + 1;
347178825Sdfr	} else {
348178825Sdfr	    append = strdup(str);
349178825Sdfr	    str = NULL;
350178825Sdfr	}
351178825Sdfr	if (append == NULL) {
352178825Sdfr	    free(*res);
353178825Sdfr	    *res = NULL;
354178825Sdfr	    krb5_set_error_string(context, "malloc - out of memory");
355178825Sdfr	    return ENOMEM;
356178825Sdfr	}
357178825Sdfr
358178825Sdfr	tlen = strlen(append);
359178825Sdfr	tmp = realloc(*res, len + tlen + 1);
360178825Sdfr	if (tmp == NULL) {
361178825Sdfr	    free(append);
362178825Sdfr	    free(*res);
363178825Sdfr	    *res = NULL;
364178825Sdfr	    krb5_set_error_string(context, "malloc - out of memory");
365178825Sdfr	    return ENOMEM;
366178825Sdfr	}
367178825Sdfr	*res = tmp;
368178825Sdfr	memcpy(*res + len, append, tlen + 1);
369178825Sdfr	len = len + tlen;
370178825Sdfr	free(append);
371178825Sdfr    }
372178825Sdfr    return 0;
373178825Sdfr}
374178825Sdfr
375178825Sdfr/*
376178825Sdfr * Return non-zero if envirnoment that will determine default krb5cc
377178825Sdfr * name has changed.
378178825Sdfr */
379178825Sdfr
380178825Sdfrstatic int
381178825Sdfrenvironment_changed(krb5_context context)
382178825Sdfr{
383178825Sdfr    const char *e;
384178825Sdfr
385178825Sdfr    /* if the cc name was set, don't change it */
386178825Sdfr    if (context->default_cc_name_set)
387178825Sdfr	return 0;
388178825Sdfr
389178825Sdfr    if(issuid())
390178825Sdfr	return 0;
391178825Sdfr
392178825Sdfr    e = getenv("KRB5CCNAME");
393178825Sdfr    if (e == NULL) {
394178825Sdfr	if (context->default_cc_name_env) {
395178825Sdfr	    free(context->default_cc_name_env);
396178825Sdfr	    context->default_cc_name_env = NULL;
397178825Sdfr	    return 1;
398178825Sdfr	}
399178825Sdfr    } else {
400178825Sdfr	if (context->default_cc_name_env == NULL)
401178825Sdfr	    return 1;
402178825Sdfr	if (strcmp(e, context->default_cc_name_env) != 0)
403178825Sdfr	    return 1;
404178825Sdfr    }
405178825Sdfr    return 0;
406178825Sdfr}
407178825Sdfr
408178825Sdfr/**
409120945Snectar * Set the default cc name for `context' to `name'.
410178825Sdfr *
411178825Sdfr * @ingroup krb5_ccache
412120945Snectar */
413120945Snectar
414178825Sdfr
415178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
416120945Snectarkrb5_cc_set_default_name(krb5_context context, const char *name)
417120945Snectar{
418120945Snectar    krb5_error_code ret = 0;
419120945Snectar    char *p;
420120945Snectar
421120945Snectar    if (name == NULL) {
422178825Sdfr	const char *e = NULL;
423178825Sdfr
424178825Sdfr	if(!issuid()) {
425178825Sdfr	    e = getenv("KRB5CCNAME");
426178825Sdfr	    if (e) {
427178825Sdfr		p = strdup(e);
428178825Sdfr		if (context->default_cc_name_env)
429178825Sdfr		    free(context->default_cc_name_env);
430178825Sdfr		context->default_cc_name_env = strdup(e);
431178825Sdfr	    }
432178825Sdfr	}
433178825Sdfr	if (e == NULL) {
434178825Sdfr	    e = krb5_config_get_string(context, NULL, "libdefaults",
435178825Sdfr				       "default_cc_name", NULL);
436178825Sdfr	    if (e) {
437178825Sdfr		ret = _krb5_expand_default_cc_name(context, e, &p);
438178825Sdfr		if (ret)
439178825Sdfr		    return ret;
440178825Sdfr	    }
441178825Sdfr	    if (e == NULL) {
442178825Sdfr		const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
443178825Sdfr		ret = (*ops->default_name)(context, &p);
444178825Sdfr		if (ret)
445178825Sdfr		    return ret;
446178825Sdfr	    }
447178825Sdfr	}
448178825Sdfr	context->default_cc_name_set = 0;
449178825Sdfr    } else {
450120945Snectar	p = strdup(name);
451178825Sdfr	context->default_cc_name_set = 1;
452178825Sdfr    }
453120945Snectar
454178825Sdfr    if (p == NULL) {
455178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
456120945Snectar	return ENOMEM;
457178825Sdfr    }
458120945Snectar
459120945Snectar    if (context->default_cc_name)
460120945Snectar	free(context->default_cc_name);
461120945Snectar
462120945Snectar    context->default_cc_name = p;
463120945Snectar
464120945Snectar    return ret;
465120945Snectar}
466120945Snectar
467178825Sdfr/**
468178825Sdfr * Return a pointer to a context static string containing the default
469178825Sdfr * ccache name.
470178825Sdfr *
471178825Sdfr * @return String to the default credential cache name.
472178825Sdfr *
473178825Sdfr * @ingroup krb5_ccache
474120945Snectar */
475120945Snectar
476178825Sdfr
477178825Sdfrconst char* KRB5_LIB_FUNCTION
47855682Smarkmkrb5_cc_default_name(krb5_context context)
47955682Smarkm{
480178825Sdfr    if (context->default_cc_name == NULL || environment_changed(context))
481120945Snectar	krb5_cc_set_default_name(context, NULL);
48255682Smarkm
483120945Snectar    return context->default_cc_name;
48455682Smarkm}
48555682Smarkm
486178825Sdfr/**
48755682Smarkm * Open the default ccache in `id'.
488178825Sdfr *
489178825Sdfr * @return Return 0 or an error code.
490178825Sdfr *
491178825Sdfr * @ingroup krb5_ccache
49255682Smarkm */
49355682Smarkm
494178825Sdfr
495178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
49655682Smarkmkrb5_cc_default(krb5_context context,
49755682Smarkm		krb5_ccache *id)
49855682Smarkm{
499120945Snectar    const char *p = krb5_cc_default_name(context);
500120945Snectar
501178825Sdfr    if (p == NULL) {
502178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
503120945Snectar	return ENOMEM;
504178825Sdfr    }
505120945Snectar    return krb5_cc_resolve(context, p, id);
50655682Smarkm}
50755682Smarkm
508178825Sdfr/**
50955682Smarkm * Create a new ccache in `id' for `primary_principal'.
510178825Sdfr *
511178825Sdfr * @return Return 0 or an error code.
512178825Sdfr *
513178825Sdfr * @ingroup krb5_ccache
51455682Smarkm */
51555682Smarkm
516178825Sdfr
517178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
51855682Smarkmkrb5_cc_initialize(krb5_context context,
51955682Smarkm		   krb5_ccache id,
52055682Smarkm		   krb5_principal primary_principal)
52155682Smarkm{
522178825Sdfr    return (*id->ops->init)(context, id, primary_principal);
52355682Smarkm}
52455682Smarkm
52555682Smarkm
526178825Sdfr/**
52755682Smarkm * Remove the ccache `id'.
528178825Sdfr *
529178825Sdfr * @return Return 0 or an error code.
530178825Sdfr *
531178825Sdfr * @ingroup krb5_ccache
53255682Smarkm */
53355682Smarkm
534178825Sdfr
535178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
53655682Smarkmkrb5_cc_destroy(krb5_context context,
53755682Smarkm		krb5_ccache id)
53855682Smarkm{
53955682Smarkm    krb5_error_code ret;
54055682Smarkm
541178825Sdfr    ret = (*id->ops->destroy)(context, id);
54255682Smarkm    krb5_cc_close (context, id);
54355682Smarkm    return ret;
54455682Smarkm}
54555682Smarkm
546178825Sdfr/**
54755682Smarkm * Stop using the ccache `id' and free the related resources.
548178825Sdfr *
549178825Sdfr * @return Return 0 or an error code.
550178825Sdfr *
551178825Sdfr * @ingroup krb5_ccache
55255682Smarkm */
55355682Smarkm
554178825Sdfr
555178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
55655682Smarkmkrb5_cc_close(krb5_context context,
55755682Smarkm	      krb5_ccache id)
55855682Smarkm{
55955682Smarkm    krb5_error_code ret;
560178825Sdfr    ret = (*id->ops->close)(context, id);
56155682Smarkm    free(id);
56255682Smarkm    return ret;
56355682Smarkm}
56455682Smarkm
565178825Sdfr/**
56655682Smarkm * Store `creds' in the ccache `id'.
567178825Sdfr *
568178825Sdfr * @return Return 0 or an error code.
569178825Sdfr *
570178825Sdfr * @ingroup krb5_ccache
57155682Smarkm */
57255682Smarkm
573178825Sdfr
574178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
57555682Smarkmkrb5_cc_store_cred(krb5_context context,
57655682Smarkm		   krb5_ccache id,
57755682Smarkm		   krb5_creds *creds)
57855682Smarkm{
579178825Sdfr    return (*id->ops->store)(context, id, creds);
58055682Smarkm}
58155682Smarkm
582178825Sdfr/**
58355682Smarkm * Retrieve the credential identified by `mcreds' (and `whichfields')
584178825Sdfr * from `id' in `creds'. 'creds' must be free by the caller using
585178825Sdfr * krb5_free_cred_contents.
586178825Sdfr *
587178825Sdfr * @return Return 0 or an error code.
588178825Sdfr *
589178825Sdfr * @ingroup krb5_ccache
59055682Smarkm */
59155682Smarkm
592178825Sdfr
593178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
59455682Smarkmkrb5_cc_retrieve_cred(krb5_context context,
59555682Smarkm		      krb5_ccache id,
59655682Smarkm		      krb5_flags whichfields,
59755682Smarkm		      const krb5_creds *mcreds,
59855682Smarkm		      krb5_creds *creds)
59955682Smarkm{
60055682Smarkm    krb5_error_code ret;
60155682Smarkm    krb5_cc_cursor cursor;
602178825Sdfr
603178825Sdfr    if (id->ops->retrieve != NULL) {
604178825Sdfr	return (*id->ops->retrieve)(context, id, whichfields,
605178825Sdfr				    mcreds, creds);
606178825Sdfr    }
607178825Sdfr
608178825Sdfr    ret = krb5_cc_start_seq_get(context, id, &cursor);
609178825Sdfr    if (ret)
610178825Sdfr	return ret;
61178527Sassar    while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
61255682Smarkm	if(krb5_compare_creds(context, whichfields, mcreds, creds)){
61355682Smarkm	    ret = 0;
61455682Smarkm	    break;
61555682Smarkm	}
616178825Sdfr	krb5_free_cred_contents (context, creds);
61755682Smarkm    }
61855682Smarkm    krb5_cc_end_seq_get(context, id, &cursor);
61955682Smarkm    return ret;
62055682Smarkm}
62155682Smarkm
622178825Sdfr/**
62355682Smarkm * Return the principal of `id' in `principal'.
624178825Sdfr *
625178825Sdfr * @return Return 0 or an error code.
626178825Sdfr *
627178825Sdfr * @ingroup krb5_ccache
62855682Smarkm */
62955682Smarkm
630178825Sdfr
631178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
63255682Smarkmkrb5_cc_get_principal(krb5_context context,
63355682Smarkm		      krb5_ccache id,
63455682Smarkm		      krb5_principal *principal)
63555682Smarkm{
636178825Sdfr    return (*id->ops->get_princ)(context, id, principal);
63755682Smarkm}
63855682Smarkm
639178825Sdfr/**
64055682Smarkm * Start iterating over `id', `cursor' is initialized to the
64155682Smarkm * beginning.
642178825Sdfr *
643178825Sdfr * @return Return 0 or an error code.
644178825Sdfr *
645178825Sdfr * @ingroup krb5_ccache
64655682Smarkm */
64755682Smarkm
648178825Sdfr
649178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
65055682Smarkmkrb5_cc_start_seq_get (krb5_context context,
65155682Smarkm		       const krb5_ccache id,
65255682Smarkm		       krb5_cc_cursor *cursor)
65355682Smarkm{
654178825Sdfr    return (*id->ops->get_first)(context, id, cursor);
65555682Smarkm}
65655682Smarkm
657178825Sdfr/**
65855682Smarkm * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
65955682Smarkm * and advance `cursor'.
660178825Sdfr *
661178825Sdfr * @return Return 0 or an error code.
662178825Sdfr *
663178825Sdfr * @ingroup krb5_ccache
66455682Smarkm */
66555682Smarkm
666178825Sdfr
667178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
66855682Smarkmkrb5_cc_next_cred (krb5_context context,
66955682Smarkm		   const krb5_ccache id,
67078527Sassar		   krb5_cc_cursor *cursor,
67178527Sassar		   krb5_creds *creds)
67255682Smarkm{
673178825Sdfr    return (*id->ops->get_next)(context, id, cursor, creds);
67455682Smarkm}
67555682Smarkm
676178825Sdfr/**
677178825Sdfr * Like krb5_cc_next_cred, but allow for selective retrieval
678178825Sdfr *
679178825Sdfr * @ingroup krb5_ccache
680178825Sdfr */
681178825Sdfr
682178825Sdfr
683178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
684178825Sdfrkrb5_cc_next_cred_match(krb5_context context,
685178825Sdfr			const krb5_ccache id,
686178825Sdfr			krb5_cc_cursor * cursor,
687178825Sdfr			krb5_creds * creds,
688178825Sdfr			krb5_flags whichfields,
689178825Sdfr			const krb5_creds * mcreds)
690178825Sdfr{
691178825Sdfr    krb5_error_code ret;
692178825Sdfr    while (1) {
693178825Sdfr	ret = krb5_cc_next_cred(context, id, cursor, creds);
694178825Sdfr	if (ret)
695178825Sdfr	    return ret;
696178825Sdfr	if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
697178825Sdfr	    return 0;
698178825Sdfr	krb5_free_cred_contents(context, creds);
699178825Sdfr    }
700178825Sdfr}
701178825Sdfr
702178825Sdfr/**
70355682Smarkm * Destroy the cursor `cursor'.
704178825Sdfr *
705178825Sdfr * @ingroup krb5_ccache
70655682Smarkm */
70755682Smarkm
708178825Sdfr
709178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
71055682Smarkmkrb5_cc_end_seq_get (krb5_context context,
71155682Smarkm		     const krb5_ccache id,
71255682Smarkm		     krb5_cc_cursor *cursor)
71355682Smarkm{
714178825Sdfr    return (*id->ops->end_get)(context, id, cursor);
71555682Smarkm}
71655682Smarkm
717178825Sdfr/**
71855682Smarkm * Remove the credential identified by `cred', `which' from `id'.
719178825Sdfr *
720178825Sdfr * @ingroup krb5_ccache
72155682Smarkm */
72255682Smarkm
723178825Sdfr
724178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
72555682Smarkmkrb5_cc_remove_cred(krb5_context context,
72655682Smarkm		    krb5_ccache id,
72755682Smarkm		    krb5_flags which,
72855682Smarkm		    krb5_creds *cred)
72955682Smarkm{
73078527Sassar    if(id->ops->remove_cred == NULL) {
73178527Sassar	krb5_set_error_string(context,
73278527Sassar			      "ccache %s does not support remove_cred",
73378527Sassar			      id->ops->prefix);
73472445Sassar	return EACCES; /* XXX */
73578527Sassar    }
73672445Sassar    return (*id->ops->remove_cred)(context, id, which, cred);
73755682Smarkm}
73855682Smarkm
739178825Sdfr/**
74055682Smarkm * Set the flags of `id' to `flags'.
741178825Sdfr *
742178825Sdfr * @ingroup krb5_ccache
74355682Smarkm */
74455682Smarkm
745178825Sdfr
746178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
74755682Smarkmkrb5_cc_set_flags(krb5_context context,
74855682Smarkm		  krb5_ccache id,
74955682Smarkm		  krb5_flags flags)
75055682Smarkm{
751178825Sdfr    return (*id->ops->set_flags)(context, id, flags);
75255682Smarkm}
75355682Smarkm
754178825Sdfr/**
75555682Smarkm * Copy the contents of `from' to `to'.
756178825Sdfr *
757178825Sdfr * @ingroup krb5_ccache
75855682Smarkm */
75955682Smarkm
760178825Sdfr
761178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
762178825Sdfrkrb5_cc_copy_cache_match(krb5_context context,
763178825Sdfr			 const krb5_ccache from,
764178825Sdfr			 krb5_ccache to,
765178825Sdfr			 krb5_flags whichfields,
766178825Sdfr			 const krb5_creds * mcreds,
767178825Sdfr			 unsigned int *matched)
76855682Smarkm{
76955682Smarkm    krb5_error_code ret;
77055682Smarkm    krb5_cc_cursor cursor;
77155682Smarkm    krb5_creds cred;
77255682Smarkm    krb5_principal princ;
77355682Smarkm
77455682Smarkm    ret = krb5_cc_get_principal(context, from, &princ);
775178825Sdfr    if (ret)
77655682Smarkm	return ret;
77755682Smarkm    ret = krb5_cc_initialize(context, to, princ);
778178825Sdfr    if (ret) {
77955682Smarkm	krb5_free_principal(context, princ);
78055682Smarkm	return ret;
78155682Smarkm    }
78255682Smarkm    ret = krb5_cc_start_seq_get(context, from, &cursor);
783178825Sdfr    if (ret) {
78455682Smarkm	krb5_free_principal(context, princ);
78555682Smarkm	return ret;
78655682Smarkm    }
787178825Sdfr    if (matched)
788178825Sdfr	*matched = 0;
789178825Sdfr    while (ret == 0 &&
790178825Sdfr	   krb5_cc_next_cred_match(context, from, &cursor, &cred,
791178825Sdfr				   whichfields, mcreds) == 0) {
792178825Sdfr	if (matched)
793178825Sdfr	    (*matched)++;
79455682Smarkm	ret = krb5_cc_store_cred(context, to, &cred);
795178825Sdfr	krb5_free_cred_contents(context, &cred);
79655682Smarkm    }
79755682Smarkm    krb5_cc_end_seq_get(context, from, &cursor);
79855682Smarkm    krb5_free_principal(context, princ);
79955682Smarkm    return ret;
80055682Smarkm}
80155682Smarkm
802178825Sdfr/**
803178825Sdfr * Just like krb5_cc_copy_cache_match, but copy everything.
804178825Sdfr *
805178825Sdfr * @ingroup krb5_ccache
806178825Sdfr */
807178825Sdfr
808178825Sdfr
809178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
810178825Sdfrkrb5_cc_copy_cache(krb5_context context,
811178825Sdfr		   const krb5_ccache from,
812178825Sdfr		   krb5_ccache to)
813178825Sdfr{
814178825Sdfr    return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
815178825Sdfr}
816178825Sdfr
817178825Sdfr/**
81855682Smarkm * Return the version of `id'.
819178825Sdfr *
820178825Sdfr * @ingroup krb5_ccache
82155682Smarkm */
82255682Smarkm
823178825Sdfr
824178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
82555682Smarkmkrb5_cc_get_version(krb5_context context,
82655682Smarkm		    const krb5_ccache id)
82755682Smarkm{
82855682Smarkm    if(id->ops->get_version)
829178825Sdfr	return (*id->ops->get_version)(context, id);
83055682Smarkm    else
83155682Smarkm	return 0;
83255682Smarkm}
833178825Sdfr
834178825Sdfr/**
835178825Sdfr * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
836178825Sdfr *
837178825Sdfr * @ingroup krb5_ccache
838178825Sdfr */
839178825Sdfr
840178825Sdfr
841178825Sdfrvoid KRB5_LIB_FUNCTION
842178825Sdfrkrb5_cc_clear_mcred(krb5_creds *mcred)
843178825Sdfr{
844178825Sdfr    memset(mcred, 0, sizeof(*mcred));
845178825Sdfr}
846178825Sdfr
847178825Sdfr/**
848178825Sdfr * Get the cc ops that is registered in `context' to handle the
849178825Sdfr * `prefix'. `prefix' can be a complete credential cache name or a
850178825Sdfr * prefix, the function will only use part up to the first colon (:)
851178825Sdfr * if there is one.
852178825Sdfr * Returns NULL if ops not found.
853178825Sdfr *
854178825Sdfr * @ingroup krb5_ccache
855178825Sdfr */
856178825Sdfr
857178825Sdfr
858178825Sdfrconst krb5_cc_ops *
859178825Sdfrkrb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
860178825Sdfr{
861178825Sdfr    char *p, *p1;
862178825Sdfr    int i;
863178825Sdfr
864178825Sdfr    if (prefix[0] == '/')
865178825Sdfr	return &krb5_fcc_ops;
866178825Sdfr
867178825Sdfr    p = strdup(prefix);
868178825Sdfr    if (p == NULL) {
869178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
870178825Sdfr	return NULL;
871178825Sdfr    }
872178825Sdfr    p1 = strchr(p, ':');
873178825Sdfr    if (p1)
874178825Sdfr	*p1 = '\0';
875178825Sdfr
876178825Sdfr    for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
877178825Sdfr	if(strcmp(context->cc_ops[i].prefix, p) == 0) {
878178825Sdfr	    free(p);
879178825Sdfr	    return &context->cc_ops[i];
880178825Sdfr	}
881178825Sdfr    }
882178825Sdfr    free(p);
883178825Sdfr    return NULL;
884178825Sdfr}
885178825Sdfr
886178825Sdfrstruct krb5_cc_cache_cursor_data {
887178825Sdfr    const krb5_cc_ops *ops;
888178825Sdfr    krb5_cc_cursor cursor;
889178825Sdfr};
890178825Sdfr
891178825Sdfr/**
892178825Sdfr * Start iterating over all caches of `type'. If `type' is NULL, the
893178825Sdfr * default type is * used. `cursor' is initialized to the beginning.
894178825Sdfr *
895178825Sdfr * @return Return 0 or an error code.
896178825Sdfr *
897178825Sdfr * @ingroup krb5_ccache
898178825Sdfr */
899178825Sdfr
900178825Sdfr
901178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
902178825Sdfrkrb5_cc_cache_get_first (krb5_context context,
903178825Sdfr			 const char *type,
904178825Sdfr			 krb5_cc_cache_cursor *cursor)
905178825Sdfr{
906178825Sdfr    const krb5_cc_ops *ops;
907178825Sdfr    krb5_error_code ret;
908178825Sdfr
909178825Sdfr    if (type == NULL)
910178825Sdfr	type = krb5_cc_default_name(context);
911178825Sdfr
912178825Sdfr    ops = krb5_cc_get_prefix_ops(context, type);
913178825Sdfr    if (ops == NULL) {
914178825Sdfr	krb5_set_error_string(context, "Unknown type \"%s\" when iterating "
915178825Sdfr			      "trying to iterate the credential caches", type);
916178825Sdfr	return KRB5_CC_UNKNOWN_TYPE;
917178825Sdfr    }
918178825Sdfr
919178825Sdfr    if (ops->get_cache_first == NULL) {
920178825Sdfr	krb5_set_error_string(context, "Credential cache type %s doesn't support "
921178825Sdfr			      "iterations over caches", ops->prefix);
922178825Sdfr	return KRB5_CC_NOSUPP;
923178825Sdfr    }
924178825Sdfr
925178825Sdfr    *cursor = calloc(1, sizeof(**cursor));
926178825Sdfr    if (*cursor == NULL) {
927178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
928178825Sdfr	return ENOMEM;
929178825Sdfr    }
930178825Sdfr
931178825Sdfr    (*cursor)->ops = ops;
932178825Sdfr
933178825Sdfr    ret = ops->get_cache_first(context, &(*cursor)->cursor);
934178825Sdfr    if (ret) {
935178825Sdfr	free(*cursor);
936178825Sdfr	*cursor = NULL;
937178825Sdfr    }
938178825Sdfr    return ret;
939178825Sdfr}
940178825Sdfr
941178825Sdfr/**
942178825Sdfr * Retrieve the next cache pointed to by (`cursor') in `id'
943178825Sdfr * and advance `cursor'.
944178825Sdfr *
945178825Sdfr * @return Return 0 or an error code.
946178825Sdfr *
947178825Sdfr * @ingroup krb5_ccache
948178825Sdfr */
949178825Sdfr
950178825Sdfr
951178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
952178825Sdfrkrb5_cc_cache_next (krb5_context context,
953178825Sdfr		   krb5_cc_cache_cursor cursor,
954178825Sdfr		   krb5_ccache *id)
955178825Sdfr{
956178825Sdfr    return cursor->ops->get_cache_next(context, cursor->cursor, id);
957178825Sdfr}
958178825Sdfr
959178825Sdfr/**
960178825Sdfr * Destroy the cursor `cursor'.
961178825Sdfr *
962178825Sdfr * @return Return 0 or an error code.
963178825Sdfr *
964178825Sdfr * @ingroup krb5_ccache
965178825Sdfr */
966178825Sdfr
967178825Sdfr
968178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
969178825Sdfrkrb5_cc_cache_end_seq_get (krb5_context context,
970178825Sdfr			   krb5_cc_cache_cursor cursor)
971178825Sdfr{
972178825Sdfr    krb5_error_code ret;
973178825Sdfr    ret = cursor->ops->end_cache_get(context, cursor->cursor);
974178825Sdfr    cursor->ops = NULL;
975178825Sdfr    free(cursor);
976178825Sdfr    return ret;
977178825Sdfr}
978178825Sdfr
979178825Sdfr/**
980178825Sdfr * Search for a matching credential cache of type `type' that have the
981178825Sdfr * `principal' as the default principal. If NULL is used for `type',
982178825Sdfr * the default type is used. On success, `id' needs to be freed with
983178825Sdfr * krb5_cc_close or krb5_cc_destroy.
984178825Sdfr *
985178825Sdfr * @return On failure, error code is returned and `id' is set to NULL.
986178825Sdfr *
987178825Sdfr * @ingroup krb5_ccache
988178825Sdfr */
989178825Sdfr
990178825Sdfr
991178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
992178825Sdfrkrb5_cc_cache_match (krb5_context context,
993178825Sdfr		     krb5_principal client,
994178825Sdfr		     const char *type,
995178825Sdfr		     krb5_ccache *id)
996178825Sdfr{
997178825Sdfr    krb5_cc_cache_cursor cursor;
998178825Sdfr    krb5_error_code ret;
999178825Sdfr    krb5_ccache cache = NULL;
1000178825Sdfr
1001178825Sdfr    *id = NULL;
1002178825Sdfr
1003178825Sdfr    ret = krb5_cc_cache_get_first (context, type, &cursor);
1004178825Sdfr    if (ret)
1005178825Sdfr	return ret;
1006178825Sdfr
1007178825Sdfr    while ((ret = krb5_cc_cache_next (context, cursor, &cache)) == 0) {
1008178825Sdfr	krb5_principal principal;
1009178825Sdfr
1010178825Sdfr	ret = krb5_cc_get_principal(context, cache, &principal);
1011178825Sdfr	if (ret == 0) {
1012178825Sdfr	    krb5_boolean match;
1013178825Sdfr
1014178825Sdfr	    match = krb5_principal_compare(context, principal, client);
1015178825Sdfr	    krb5_free_principal(context, principal);
1016178825Sdfr	    if (match)
1017178825Sdfr		break;
1018178825Sdfr	}
1019178825Sdfr
1020178825Sdfr	krb5_cc_close(context, cache);
1021178825Sdfr	cache = NULL;
1022178825Sdfr    }
1023178825Sdfr
1024178825Sdfr    krb5_cc_cache_end_seq_get(context, cursor);
1025178825Sdfr
1026178825Sdfr    if (cache == NULL) {
1027178825Sdfr	char *str;
1028178825Sdfr
1029178825Sdfr	krb5_unparse_name(context, client, &str);
1030178825Sdfr
1031178825Sdfr	krb5_set_error_string(context, "Principal %s not found in a "
1032178825Sdfr			  "credential cache", str ? str : "<out of memory>");
1033178825Sdfr	if (str)
1034178825Sdfr	    free(str);
1035178825Sdfr	return KRB5_CC_NOTFOUND;
1036178825Sdfr    }
1037178825Sdfr    *id = cache;
1038178825Sdfr
1039178825Sdfr    return 0;
1040178825Sdfr}
1041178825Sdfr
1042178825Sdfr/**
1043178825Sdfr * Move the content from one credential cache to another. The
1044178825Sdfr * operation is an atomic switch.
1045178825Sdfr *
1046178825Sdfr * @param context a Keberos context
1047178825Sdfr * @param from the credential cache to move the content from
1048178825Sdfr * @param to the credential cache to move the content to
1049178825Sdfr
1050178825Sdfr * @return On sucess, from is freed. On failure, error code is
1051178825Sdfr * returned and from and to are both still allocated.
1052178825Sdfr *
1053178825Sdfr * @ingroup krb5_ccache
1054178825Sdfr */
1055178825Sdfr
1056178825Sdfrkrb5_error_code
1057178825Sdfrkrb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
1058178825Sdfr{
1059178825Sdfr    krb5_error_code ret;
1060178825Sdfr
1061178825Sdfr    if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
1062178825Sdfr	krb5_set_error_string(context, "Moving credentials between diffrent "
1063178825Sdfr			      "types not yet supported");
1064178825Sdfr	return KRB5_CC_NOSUPP;
1065178825Sdfr    }
1066178825Sdfr
1067178825Sdfr    ret = (*to->ops->move)(context, from, to);
1068178825Sdfr    if (ret == 0) {
1069178825Sdfr	memset(from, 0, sizeof(*from));
1070178825Sdfr	free(from);
1071178825Sdfr    }
1072178825Sdfr    return ret;
1073178825Sdfr}
1074