1505d05c7Sgtb /*
2159d09a2SMark Phalan  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3505d05c7Sgtb  * Use is subject to license terms.
4505d05c7Sgtb  */
5505d05c7Sgtb 
6505d05c7Sgtb 
7505d05c7Sgtb /*
8505d05c7Sgtb  * lib/krb5/ccache/cc_memory.c
9505d05c7Sgtb  *
10505d05c7Sgtb  * Copyright 1990,1991,2000,2004 by the Massachusetts Institute of Technology.
11505d05c7Sgtb  * All Rights Reserved.
12505d05c7Sgtb  *
13505d05c7Sgtb  * Export of this software from the United States of America may
14505d05c7Sgtb  *   require a specific license from the United States Government.
15505d05c7Sgtb  *   It is the responsibility of any person or organization contemplating
16505d05c7Sgtb  *   export to obtain such a license before exporting.
17*55fea89dSDan Cross  *
18505d05c7Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19505d05c7Sgtb  * distribute this software and its documentation for any purpose and
20505d05c7Sgtb  * without fee is hereby granted, provided that the above copyright
21505d05c7Sgtb  * notice appear in all copies and that both that copyright notice and
22505d05c7Sgtb  * this permission notice appear in supporting documentation, and that
23505d05c7Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
24505d05c7Sgtb  * to distribution of the software without specific, written prior
25505d05c7Sgtb  * permission.  Furthermore if you modify this software you must label
26505d05c7Sgtb  * your software as modified software and not distribute it in such a
27505d05c7Sgtb  * fashion that it might be confused with the original M.I.T. software.
28505d05c7Sgtb  * M.I.T. makes no representations about the suitability of
29505d05c7Sgtb  * this software for any purpose.  It is provided "as is" without express
30505d05c7Sgtb  * or implied warranty.
31*55fea89dSDan Cross  *
32505d05c7Sgtb  *
33505d05c7Sgtb  * implementation of memory-based credentials cache
34505d05c7Sgtb  */
35505d05c7Sgtb #include "k5-int.h"
36505d05c7Sgtb #include <errno.h>
37505d05c7Sgtb 
38505d05c7Sgtb static krb5_error_code KRB5_CALLCONV krb5_mcc_close
39505d05c7Sgtb 	(krb5_context, krb5_ccache id );
40505d05c7Sgtb 
41*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_destroy
42505d05c7Sgtb 	(krb5_context, krb5_ccache id );
43505d05c7Sgtb 
44*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_end_seq_get
45505d05c7Sgtb 	(krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
46505d05c7Sgtb 
47*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_generate_new
48505d05c7Sgtb 	(krb5_context, krb5_ccache *id );
49505d05c7Sgtb 
50*55fea89dSDan Cross static const char * KRB5_CALLCONV krb5_mcc_get_name
51505d05c7Sgtb 	(krb5_context, krb5_ccache id );
52505d05c7Sgtb 
53*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_get_principal
54505d05c7Sgtb 	(krb5_context, krb5_ccache id , krb5_principal *princ );
55505d05c7Sgtb 
56*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_initialize
57505d05c7Sgtb 	(krb5_context, krb5_ccache id , krb5_principal princ );
58505d05c7Sgtb 
59*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_next_cred
60*55fea89dSDan Cross 	(krb5_context,
61*55fea89dSDan Cross 		   krb5_ccache id ,
62*55fea89dSDan Cross 		   krb5_cc_cursor *cursor ,
63505d05c7Sgtb 		   krb5_creds *creds );
64505d05c7Sgtb 
65*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_resolve
66505d05c7Sgtb 	(krb5_context, krb5_ccache *id , const char *residual );
67505d05c7Sgtb 
68*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_retrieve
69*55fea89dSDan Cross 	(krb5_context,
70*55fea89dSDan Cross 		   krb5_ccache id ,
71*55fea89dSDan Cross 		   krb5_flags whichfields ,
72*55fea89dSDan Cross 		   krb5_creds *mcreds ,
73505d05c7Sgtb 		   krb5_creds *creds );
74505d05c7Sgtb 
75*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_start_seq_get
76505d05c7Sgtb 	(krb5_context, krb5_ccache id , krb5_cc_cursor *cursor );
77505d05c7Sgtb 
78*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_store
79505d05c7Sgtb 	(krb5_context, krb5_ccache id , krb5_creds *creds );
80505d05c7Sgtb 
81*55fea89dSDan Cross static krb5_error_code KRB5_CALLCONV krb5_mcc_set_flags
82505d05c7Sgtb 	(krb5_context, krb5_ccache id , krb5_flags flags );
83505d05c7Sgtb 
84159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_new(
85159d09a2SMark Phalan     krb5_context,
86159d09a2SMark Phalan     krb5_cc_ptcursor *);
87159d09a2SMark Phalan 
88159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_next(
89159d09a2SMark Phalan     krb5_context,
90159d09a2SMark Phalan     krb5_cc_ptcursor,
91159d09a2SMark Phalan     krb5_ccache *);
92159d09a2SMark Phalan 
93159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV krb5_mcc_ptcursor_free(
94159d09a2SMark Phalan     krb5_context,
95159d09a2SMark Phalan     krb5_cc_ptcursor *);
96159d09a2SMark Phalan 
97505d05c7Sgtb extern const krb5_cc_ops krb5_mcc_ops;
98505d05c7Sgtb extern krb5_error_code krb5_change_cache (void);
99505d05c7Sgtb 
100505d05c7Sgtb #define KRB5_OK 0
101505d05c7Sgtb 
102505d05c7Sgtb typedef struct _krb5_mcc_link {
103505d05c7Sgtb     struct _krb5_mcc_link *next;
104505d05c7Sgtb     krb5_creds *creds;
105505d05c7Sgtb } krb5_mcc_link, *krb5_mcc_cursor;
106505d05c7Sgtb 
107505d05c7Sgtb typedef struct _krb5_mcc_data {
108505d05c7Sgtb     char *name;
109505d05c7Sgtb     k5_mutex_t lock;
110505d05c7Sgtb     krb5_principal prin;
111505d05c7Sgtb     krb5_mcc_cursor link;
112505d05c7Sgtb } krb5_mcc_data;
113505d05c7Sgtb 
114505d05c7Sgtb typedef struct krb5_mcc_list_node {
115505d05c7Sgtb     struct krb5_mcc_list_node *next;
116505d05c7Sgtb     krb5_mcc_data *cache;
117505d05c7Sgtb } krb5_mcc_list_node;
118505d05c7Sgtb 
119159d09a2SMark Phalan struct krb5_mcc_ptcursor_data {
120159d09a2SMark Phalan     struct krb5_mcc_list_node *cur;
121159d09a2SMark Phalan };
122159d09a2SMark Phalan 
123505d05c7Sgtb k5_mutex_t krb5int_mcc_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
124505d05c7Sgtb static krb5_mcc_list_node *mcc_head = 0;
125505d05c7Sgtb 
126505d05c7Sgtb /*
127505d05c7Sgtb  * Modifies:
128505d05c7Sgtb  * id
129505d05c7Sgtb  *
130505d05c7Sgtb  * Effects:
131505d05c7Sgtb  * Creates/refreshes the file cred cache id.  If the cache exists, its
132505d05c7Sgtb  * contents are destroyed.
133505d05c7Sgtb  *
134505d05c7Sgtb  * Errors:
135505d05c7Sgtb  * system errors
136505d05c7Sgtb  * permission errors
137505d05c7Sgtb  */
138505d05c7Sgtb static void krb5_mcc_free (krb5_context context, krb5_ccache id);
139505d05c7Sgtb 
140505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_initialize(krb5_context context,krb5_ccache id,krb5_principal princ)141505d05c7Sgtb krb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
142505d05c7Sgtb {
143*55fea89dSDan Cross     krb5_error_code ret;
144505d05c7Sgtb 
145505d05c7Sgtb     krb5_mcc_free(context, id);
146505d05c7Sgtb     ret = krb5_copy_principal(context, princ,
147505d05c7Sgtb 			      &((krb5_mcc_data *)id->data)->prin);
148505d05c7Sgtb     if (ret == KRB5_OK)
149505d05c7Sgtb         krb5_change_cache();
150505d05c7Sgtb     return ret;
151505d05c7Sgtb }
152505d05c7Sgtb 
153505d05c7Sgtb /*
154505d05c7Sgtb  * Modifies:
155505d05c7Sgtb  * id
156505d05c7Sgtb  *
157505d05c7Sgtb  * Effects:
158505d05c7Sgtb  * Closes the file cache, invalidates the id, and frees any resources
159505d05c7Sgtb  * associated with the cache.
160505d05c7Sgtb  */
161505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_close(krb5_context context,krb5_ccache id)162505d05c7Sgtb krb5_mcc_close(krb5_context context, krb5_ccache id)
163505d05c7Sgtb {
164505d05c7Sgtb      krb5_xfree(id);
165505d05c7Sgtb      return KRB5_OK;
166505d05c7Sgtb }
167505d05c7Sgtb 
168505d05c7Sgtb void
krb5_mcc_free(krb5_context context,krb5_ccache id)169505d05c7Sgtb krb5_mcc_free(krb5_context context, krb5_ccache id)
170505d05c7Sgtb {
171505d05c7Sgtb     krb5_mcc_cursor curr,next;
172505d05c7Sgtb     krb5_mcc_data *d;
173505d05c7Sgtb 
174505d05c7Sgtb     d = (krb5_mcc_data *) id->data;
175505d05c7Sgtb     for (curr = d->link; curr;) {
176505d05c7Sgtb 	krb5_free_creds(context, curr->creds);
177505d05c7Sgtb 	next = curr->next;
178505d05c7Sgtb 	krb5_xfree(curr);
179505d05c7Sgtb 	curr = next;
180505d05c7Sgtb     }
181505d05c7Sgtb     d->link = NULL;
182505d05c7Sgtb     krb5_free_principal(context, d->prin);
183505d05c7Sgtb }
184505d05c7Sgtb 
185505d05c7Sgtb /*
186505d05c7Sgtb  * Effects:
187505d05c7Sgtb  * Destroys the contents of id.
188505d05c7Sgtb  *
189505d05c7Sgtb  * Errors:
190505d05c7Sgtb  * none
191505d05c7Sgtb  */
192505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_destroy(krb5_context context,krb5_ccache id)193505d05c7Sgtb krb5_mcc_destroy(krb5_context context, krb5_ccache id)
194505d05c7Sgtb {
195505d05c7Sgtb     krb5_mcc_list_node **curr, *node;
196505d05c7Sgtb     krb5_mcc_data *d;
197505d05c7Sgtb     krb5_error_code err;
198505d05c7Sgtb 
199505d05c7Sgtb     err = k5_mutex_lock(&krb5int_mcc_mutex);
200505d05c7Sgtb     if (err)
201505d05c7Sgtb 	return err;
202505d05c7Sgtb 
203505d05c7Sgtb     d = (krb5_mcc_data *)id->data;
204505d05c7Sgtb     for (curr = &mcc_head; *curr; curr = &(*curr)->next) {
205505d05c7Sgtb 	if ((*curr)->cache == d) {
206505d05c7Sgtb 	    node = *curr;
207505d05c7Sgtb 	    *curr = node->next;
208505d05c7Sgtb 	    free(node);
209505d05c7Sgtb 	    break;
210505d05c7Sgtb 	}
211505d05c7Sgtb     }
212505d05c7Sgtb     k5_mutex_unlock(&krb5int_mcc_mutex);
213505d05c7Sgtb 
214505d05c7Sgtb     krb5_mcc_free(context, id);
215505d05c7Sgtb     krb5_xfree(d->name);
216505d05c7Sgtb     k5_mutex_destroy(&d->lock);
217*55fea89dSDan Cross     krb5_xfree(d);
218505d05c7Sgtb     krb5_xfree(id);
219505d05c7Sgtb 
220505d05c7Sgtb     krb5_change_cache ();
221505d05c7Sgtb     return KRB5_OK;
222505d05c7Sgtb }
223505d05c7Sgtb 
224505d05c7Sgtb /*
225505d05c7Sgtb  * Requires:
226505d05c7Sgtb  * residual is a legal path name, and a null-terminated string
227505d05c7Sgtb  *
228505d05c7Sgtb  * Modifies:
229505d05c7Sgtb  * id
230*55fea89dSDan Cross  *
231505d05c7Sgtb  * Effects:
232505d05c7Sgtb  * creates a file-based cred cache that will reside in the file
233505d05c7Sgtb  * residual.  The cache is not opened, but the filename is reserved.
234*55fea89dSDan Cross  *
235505d05c7Sgtb  * Returns:
236505d05c7Sgtb  * A filled in krb5_ccache structure "id".
237505d05c7Sgtb  *
238505d05c7Sgtb  * Errors:
239505d05c7Sgtb  * KRB5_CC_NOMEM - there was insufficient memory to allocate the
240505d05c7Sgtb  *              krb5_ccache.  id is undefined.
241505d05c7Sgtb  * permission errors
242505d05c7Sgtb  */
243505d05c7Sgtb static krb5_error_code new_mcc_data (const char *, krb5_mcc_data **);
244505d05c7Sgtb 
245505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_resolve(krb5_context context,krb5_ccache * id,const char * residual)246505d05c7Sgtb krb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
247505d05c7Sgtb {
248505d05c7Sgtb     krb5_ccache lid;
249505d05c7Sgtb     krb5_mcc_list_node *ptr;
250505d05c7Sgtb     krb5_error_code err;
251505d05c7Sgtb     krb5_mcc_data *d;
252505d05c7Sgtb 
253505d05c7Sgtb     lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
254505d05c7Sgtb     if (lid == NULL)
255505d05c7Sgtb 	return KRB5_CC_NOMEM;
256505d05c7Sgtb 
257505d05c7Sgtb     lid->ops = &krb5_mcc_ops;
258505d05c7Sgtb 
259505d05c7Sgtb     err = k5_mutex_lock(&krb5int_mcc_mutex);
260505d05c7Sgtb     if (err) {
261159d09a2SMark Phalan         /* Solaris Kerberos - fix mem leak */
262505d05c7Sgtb         krb5_xfree(lid);
263505d05c7Sgtb 	return err;
264505d05c7Sgtb     }
265505d05c7Sgtb     for (ptr = mcc_head; ptr; ptr=ptr->next)
266505d05c7Sgtb 	if (!strcmp(ptr->cache->name, residual))
267505d05c7Sgtb 	    break;
268505d05c7Sgtb     if (ptr)
269505d05c7Sgtb 	d = ptr->cache;
270505d05c7Sgtb     else {
271505d05c7Sgtb 	err = new_mcc_data(residual, &d);
272505d05c7Sgtb 	if (err) {
273505d05c7Sgtb 	    k5_mutex_unlock(&krb5int_mcc_mutex);
274505d05c7Sgtb 	    krb5_xfree(lid);
275505d05c7Sgtb 	    return err;
276505d05c7Sgtb 	}
277505d05c7Sgtb     }
278505d05c7Sgtb     k5_mutex_unlock(&krb5int_mcc_mutex);
279505d05c7Sgtb     lid->data = d;
280*55fea89dSDan Cross     *id = lid;
281505d05c7Sgtb     return KRB5_OK;
282505d05c7Sgtb }
283505d05c7Sgtb 
284505d05c7Sgtb /*
285505d05c7Sgtb  * Effects:
286505d05c7Sgtb  * Prepares for a sequential search of the credentials cache.
287505d05c7Sgtb  * Returns a krb5_cc_cursor to be used with krb5_mcc_next_cred and
288505d05c7Sgtb  * krb5_mcc_end_seq_get.
289505d05c7Sgtb  *
290505d05c7Sgtb  * If the cache is modified between the time of this call and the time
291505d05c7Sgtb  * of the final krb5_mcc_end_seq_get, the results are undefined.
292505d05c7Sgtb  *
293505d05c7Sgtb  * Errors:
294505d05c7Sgtb  * KRB5_CC_NOMEM
295505d05c7Sgtb  * system errors
296505d05c7Sgtb  */
297505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_start_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)298505d05c7Sgtb krb5_mcc_start_seq_get(krb5_context context, krb5_ccache id,
299505d05c7Sgtb 		       krb5_cc_cursor *cursor)
300505d05c7Sgtb {
301505d05c7Sgtb      krb5_mcc_cursor mcursor;
302505d05c7Sgtb      krb5_error_code err;
303505d05c7Sgtb      krb5_mcc_data *d;
304505d05c7Sgtb 
305505d05c7Sgtb      d = id->data;
306505d05c7Sgtb      err = k5_mutex_lock(&d->lock);
307505d05c7Sgtb      if (err)
308505d05c7Sgtb 	 return err;
309505d05c7Sgtb      mcursor = d->link;
310505d05c7Sgtb      k5_mutex_unlock(&d->lock);
311505d05c7Sgtb      *cursor = (krb5_cc_cursor) mcursor;
312505d05c7Sgtb      return KRB5_OK;
313505d05c7Sgtb }
314505d05c7Sgtb 
315505d05c7Sgtb /*
316505d05c7Sgtb  * Requires:
317505d05c7Sgtb  * cursor is a krb5_cc_cursor originally obtained from
318505d05c7Sgtb  * krb5_mcc_start_seq_get.
319505d05c7Sgtb  *
320505d05c7Sgtb  * Modifes:
321505d05c7Sgtb  * cursor, creds
322*55fea89dSDan Cross  *
323505d05c7Sgtb  * Effects:
324505d05c7Sgtb  * Fills in creds with the "next" credentals structure from the cache
325505d05c7Sgtb  * id.  The actual order the creds are returned in is arbitrary.
326505d05c7Sgtb  * Space is allocated for the variable length fields in the
327505d05c7Sgtb  * credentials structure, so the object returned must be passed to
328505d05c7Sgtb  * krb5_destroy_credential.
329505d05c7Sgtb  *
330505d05c7Sgtb  * The cursor is updated for the next call to krb5_mcc_next_cred.
331505d05c7Sgtb  *
332505d05c7Sgtb  * Errors:
333505d05c7Sgtb  * system errors
334505d05c7Sgtb  */
335505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_next_cred(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor,krb5_creds * creds)336505d05c7Sgtb krb5_mcc_next_cred(krb5_context context, krb5_ccache id,
337505d05c7Sgtb 		   krb5_cc_cursor *cursor, krb5_creds *creds)
338505d05c7Sgtb {
339505d05c7Sgtb      krb5_mcc_cursor mcursor;
340505d05c7Sgtb      krb5_error_code retval;
341505d05c7Sgtb      krb5_data *scratch;
342505d05c7Sgtb 
343505d05c7Sgtb      /* Once the node in the linked list is created, it's never
344505d05c7Sgtb 	modified, so we don't need to worry about locking here.  (Note
345505d05c7Sgtb 	that we don't support _remove_cred.)  */
346505d05c7Sgtb      mcursor = (krb5_mcc_cursor) *cursor;
347505d05c7Sgtb      if (mcursor == NULL)
348505d05c7Sgtb 	return KRB5_CC_END;
349*55fea89dSDan Cross      memset(creds, 0, sizeof(krb5_creds));
350505d05c7Sgtb      if (mcursor->creds) {
351505d05c7Sgtb 	*creds = *mcursor->creds;
352505d05c7Sgtb 	retval = krb5_copy_principal(context, mcursor->creds->client, &creds->client);
353505d05c7Sgtb 	if (retval)
354505d05c7Sgtb 		return retval;
355505d05c7Sgtb 	retval = krb5_copy_principal(context, mcursor->creds->server,
356505d05c7Sgtb 		&creds->server);
357505d05c7Sgtb 	if (retval)
358505d05c7Sgtb 		goto cleanclient;
359505d05c7Sgtb 	retval = krb5_copy_keyblock_contents(context, &mcursor->creds->keyblock,
360505d05c7Sgtb 		&creds->keyblock);
361505d05c7Sgtb 	if (retval)
362505d05c7Sgtb 		goto cleanserver;
363505d05c7Sgtb 	retval = krb5_copy_addresses(context, mcursor->creds->addresses,
364505d05c7Sgtb 		&creds->addresses);
365505d05c7Sgtb 	if (retval)
366505d05c7Sgtb 		goto cleanblock;
367505d05c7Sgtb 	retval = krb5_copy_data(context, &mcursor->creds->ticket, &scratch);
368505d05c7Sgtb 	if (retval)
369505d05c7Sgtb 		goto cleanaddrs;
370505d05c7Sgtb 	creds->ticket = *scratch;
371505d05c7Sgtb 	krb5_xfree(scratch);
372505d05c7Sgtb 	retval = krb5_copy_data(context, &mcursor->creds->second_ticket, &scratch);
373505d05c7Sgtb 	if (retval)
374505d05c7Sgtb 		goto cleanticket;
375505d05c7Sgtb 	creds->second_ticket = *scratch;
376505d05c7Sgtb 	krb5_xfree(scratch);
377505d05c7Sgtb 	retval = krb5_copy_authdata(context, mcursor->creds->authdata,
378505d05c7Sgtb 		&creds->authdata);
379505d05c7Sgtb 	if (retval)
380505d05c7Sgtb 		goto clearticket;
381505d05c7Sgtb      }
382505d05c7Sgtb      *cursor = (krb5_cc_cursor)mcursor->next;
383505d05c7Sgtb      return KRB5_OK;
384505d05c7Sgtb 
385505d05c7Sgtb clearticket:
386505d05c7Sgtb 	memset(creds->ticket.data,0, (unsigned) creds->ticket.length);
387505d05c7Sgtb cleanticket:
388505d05c7Sgtb 	krb5_xfree(creds->ticket.data);
389505d05c7Sgtb cleanaddrs:
390505d05c7Sgtb 	krb5_free_addresses(context, creds->addresses);
391505d05c7Sgtb cleanblock:
392505d05c7Sgtb 	krb5_xfree(creds->keyblock.contents);
393505d05c7Sgtb cleanserver:
394505d05c7Sgtb 	krb5_free_principal(context, creds->server);
395505d05c7Sgtb cleanclient:
396505d05c7Sgtb 	krb5_free_principal(context, creds->client);
397505d05c7Sgtb 	return retval;
398505d05c7Sgtb }
399505d05c7Sgtb 
400505d05c7Sgtb /*
401505d05c7Sgtb  * Requires:
402505d05c7Sgtb  * cursor is a krb5_cc_cursor originally obtained from
403505d05c7Sgtb  * krb5_mcc_start_seq_get.
404505d05c7Sgtb  *
405505d05c7Sgtb  * Modifies:
406505d05c7Sgtb  * id, cursor
407505d05c7Sgtb  *
408505d05c7Sgtb  * Effects:
409505d05c7Sgtb  * Finishes sequential processing of the file credentials ccache id,
410505d05c7Sgtb  * and invalidates the cursor (it must never be used after this call).
411505d05c7Sgtb  */
412505d05c7Sgtb /* ARGSUSED */
413505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_end_seq_get(krb5_context context,krb5_ccache id,krb5_cc_cursor * cursor)414505d05c7Sgtb krb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
415505d05c7Sgtb {
416505d05c7Sgtb      *cursor = 0L;
417505d05c7Sgtb      return KRB5_OK;
418505d05c7Sgtb }
419505d05c7Sgtb 
420505d05c7Sgtb /* Utility routine: Creates the back-end data for a memory cache, and
421505d05c7Sgtb    threads it into the global linked list.
422505d05c7Sgtb 
423505d05c7Sgtb    Call with the global list lock held.  */
424505d05c7Sgtb static krb5_error_code
new_mcc_data(const char * name,krb5_mcc_data ** dataptr)425505d05c7Sgtb new_mcc_data (const char *name, krb5_mcc_data **dataptr)
426505d05c7Sgtb {
427505d05c7Sgtb     krb5_error_code err;
428505d05c7Sgtb     krb5_mcc_data *d;
429505d05c7Sgtb     krb5_mcc_list_node *n;
430505d05c7Sgtb 
431505d05c7Sgtb     d = malloc(sizeof(krb5_mcc_data));
432505d05c7Sgtb     if (d == NULL)
433505d05c7Sgtb 	return KRB5_CC_NOMEM;
434505d05c7Sgtb 
435505d05c7Sgtb     err = k5_mutex_init(&d->lock);
436505d05c7Sgtb     if (err) {
437505d05c7Sgtb 	krb5_xfree(d);
438505d05c7Sgtb 	return err;
439505d05c7Sgtb     }
440505d05c7Sgtb 
441505d05c7Sgtb     d->name = malloc(strlen(name) + 1);
442505d05c7Sgtb     if (d->name == NULL) {
443505d05c7Sgtb 	k5_mutex_destroy(&d->lock);
444505d05c7Sgtb 	krb5_xfree(d);
445505d05c7Sgtb 	return KRB5_CC_NOMEM;
446505d05c7Sgtb     }
447505d05c7Sgtb     d->link = NULL;
448505d05c7Sgtb     d->prin = NULL;
449505d05c7Sgtb 
450505d05c7Sgtb     /* Set up the filename */
451505d05c7Sgtb     strcpy(d->name, name);
452505d05c7Sgtb 
453505d05c7Sgtb     n = malloc(sizeof(krb5_mcc_list_node));
454505d05c7Sgtb     if (n == NULL) {
455505d05c7Sgtb 	free(d->name);
456505d05c7Sgtb 	k5_mutex_destroy(&d->lock);
457505d05c7Sgtb 	free(d);
458505d05c7Sgtb 	return KRB5_CC_NOMEM;
459505d05c7Sgtb     }
460505d05c7Sgtb 
461505d05c7Sgtb     n->cache = d;
462505d05c7Sgtb     n->next = mcc_head;
463505d05c7Sgtb     mcc_head = n;
464505d05c7Sgtb 
465505d05c7Sgtb     *dataptr = d;
466505d05c7Sgtb     return 0;
467505d05c7Sgtb }
468505d05c7Sgtb 
469159d09a2SMark Phalan static krb5_error_code random_string (krb5_context, char *, unsigned int);
470*55fea89dSDan Cross 
471505d05c7Sgtb /*
472505d05c7Sgtb  * Effects:
473505d05c7Sgtb  * Creates a new file cred cache whose name is guaranteed to be
474505d05c7Sgtb  * unique.  The name begins with the string TKT_ROOT (from mcc.h).
475505d05c7Sgtb  * The cache is not opened, but the new filename is reserved.
476*55fea89dSDan Cross  *
477505d05c7Sgtb  * Returns:
478505d05c7Sgtb  * The filled in krb5_ccache id.
479505d05c7Sgtb  *
480505d05c7Sgtb  * Errors:
481505d05c7Sgtb  * KRB5_CC_NOMEM - there was insufficient memory to allocate the
482505d05c7Sgtb  *              krb5_ccache.  id is undefined.
483505d05c7Sgtb  * system errors (from open)
484505d05c7Sgtb  */
485159d09a2SMark Phalan 
486505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_generate_new(krb5_context context,krb5_ccache * id)487505d05c7Sgtb krb5_mcc_generate_new (krb5_context context, krb5_ccache *id)
488505d05c7Sgtb {
489505d05c7Sgtb     krb5_ccache lid;
490159d09a2SMark Phalan     char uniquename[8];
491505d05c7Sgtb     krb5_error_code err;
492505d05c7Sgtb     krb5_mcc_data *d;
493505d05c7Sgtb 
494505d05c7Sgtb     /* Allocate memory */
495505d05c7Sgtb     lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
496505d05c7Sgtb     if (lid == NULL)
497505d05c7Sgtb 	return KRB5_CC_NOMEM;
498505d05c7Sgtb 
499505d05c7Sgtb     lid->ops = &krb5_mcc_ops;
500*55fea89dSDan Cross 
501505d05c7Sgtb     err = k5_mutex_lock(&krb5int_mcc_mutex);
502505d05c7Sgtb     if (err) {
503505d05c7Sgtb 	free(lid);
504505d05c7Sgtb 	return err;
505505d05c7Sgtb     }
506*55fea89dSDan Cross 
507159d09a2SMark Phalan     /* Check for uniqueness with mutex locked to avoid race conditions */
508159d09a2SMark Phalan     while (1) {
509159d09a2SMark Phalan         krb5_mcc_list_node *ptr;
510159d09a2SMark Phalan 
511159d09a2SMark Phalan         random_string (context, uniquename, sizeof (uniquename));
512*55fea89dSDan Cross 
513159d09a2SMark Phalan 	for (ptr = mcc_head; ptr; ptr=ptr->next) {
514159d09a2SMark Phalan             if (!strcmp(ptr->cache->name, uniquename)) {
515159d09a2SMark Phalan 		break;  /* got a match, loop again */
516159d09a2SMark Phalan             }
517159d09a2SMark Phalan 	}
518159d09a2SMark Phalan         if (!ptr) break; /* got to the end without finding a match */
519159d09a2SMark Phalan     }
520*55fea89dSDan Cross 
521159d09a2SMark Phalan     err = new_mcc_data(uniquename, &d);
522159d09a2SMark Phalan 
523505d05c7Sgtb     k5_mutex_unlock(&krb5int_mcc_mutex);
524505d05c7Sgtb     if (err) {
525505d05c7Sgtb 	krb5_xfree(lid);
526505d05c7Sgtb 	return err;
527505d05c7Sgtb     }
528505d05c7Sgtb     lid->data = d;
529159d09a2SMark Phalan     *id = lid;
530505d05c7Sgtb     krb5_change_cache ();
531505d05c7Sgtb     return KRB5_OK;
532505d05c7Sgtb }
533505d05c7Sgtb 
534*55fea89dSDan Cross /* Utility routine: Creates a random memory ccache name.
535*55fea89dSDan Cross  * This algorithm was selected because it creates readable
536159d09a2SMark Phalan  * random ccache names in a fixed size buffer.  */
537159d09a2SMark Phalan 
538159d09a2SMark Phalan static krb5_error_code
random_string(krb5_context context,char * string,unsigned int length)539159d09a2SMark Phalan random_string (krb5_context context, char *string, unsigned int length)
540159d09a2SMark Phalan {
541159d09a2SMark Phalan     static const unsigned char charlist[] =
542159d09a2SMark Phalan         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
543159d09a2SMark Phalan     krb5_error_code err = 0;
544159d09a2SMark Phalan     unsigned char *bytes = NULL;
545159d09a2SMark Phalan     unsigned int bytecount = length - 1;
546*55fea89dSDan Cross 
547159d09a2SMark Phalan     if (!err) {
548159d09a2SMark Phalan         bytes = malloc (bytecount);
549159d09a2SMark Phalan         if (bytes == NULL) { err = ENOMEM; }
550159d09a2SMark Phalan     }
551*55fea89dSDan Cross 
552159d09a2SMark Phalan     if (!err) {
553159d09a2SMark Phalan         krb5_data data;
554159d09a2SMark Phalan         data.length = bytecount;
555159d09a2SMark Phalan         data.data = (char *) bytes;
556159d09a2SMark Phalan         err = krb5_c_random_make_octets (context, &data);
557159d09a2SMark Phalan     }
558*55fea89dSDan Cross 
559159d09a2SMark Phalan     if (!err) {
560159d09a2SMark Phalan         unsigned int i;
561159d09a2SMark Phalan         for (i = 0; i < bytecount; i++) {
562159d09a2SMark Phalan             string [i] = charlist[bytes[i] % (sizeof (charlist) - 1)];
563159d09a2SMark Phalan         }
564159d09a2SMark Phalan         string[length - 1] = '\0';
565159d09a2SMark Phalan     }
566*55fea89dSDan Cross 
567159d09a2SMark Phalan     if (bytes != NULL) { free (bytes); }
568*55fea89dSDan Cross 
569159d09a2SMark Phalan     return err;
570159d09a2SMark Phalan }
571159d09a2SMark Phalan 
572505d05c7Sgtb /*
573505d05c7Sgtb  * Requires:
574505d05c7Sgtb  * id is a file credential cache
575*55fea89dSDan Cross  *
576505d05c7Sgtb  * Returns:
577505d05c7Sgtb  * The name of the file cred cache id.
578505d05c7Sgtb  */
579505d05c7Sgtb const char * KRB5_CALLCONV
krb5_mcc_get_name(krb5_context context,krb5_ccache id)580505d05c7Sgtb krb5_mcc_get_name (krb5_context context, krb5_ccache id)
581505d05c7Sgtb {
582505d05c7Sgtb      return (char *) ((krb5_mcc_data *) id->data)->name;
583505d05c7Sgtb }
584505d05c7Sgtb 
585505d05c7Sgtb /*
586505d05c7Sgtb  * Modifies:
587505d05c7Sgtb  * id, princ
588505d05c7Sgtb  *
589505d05c7Sgtb  * Effects:
590505d05c7Sgtb  * Retrieves the primary principal from id, as set with
591505d05c7Sgtb  * krb5_mcc_initialize.  The principal is returned is allocated
592505d05c7Sgtb  * storage that must be freed by the caller via krb5_free_principal.
593505d05c7Sgtb  *
594505d05c7Sgtb  * Errors:
595505d05c7Sgtb  * system errors
596505d05c7Sgtb  * KRB5_CC_NOMEM
597505d05c7Sgtb  */
598505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_get_principal(krb5_context context,krb5_ccache id,krb5_principal * princ)599505d05c7Sgtb krb5_mcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
600505d05c7Sgtb {
601505d05c7Sgtb      krb5_mcc_data *ptr = (krb5_mcc_data *)id->data;
602505d05c7Sgtb      if (!ptr->prin) {
603505d05c7Sgtb         *princ = 0L;
604505d05c7Sgtb         return KRB5_FCC_NOFILE;
605505d05c7Sgtb      }
606505d05c7Sgtb      return krb5_copy_principal(context, ptr->prin, princ);
607505d05c7Sgtb }
608505d05c7Sgtb 
609505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_retrieve(krb5_context context,krb5_ccache id,krb5_flags whichfields,krb5_creds * mcreds,krb5_creds * creds)610505d05c7Sgtb krb5_mcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
611505d05c7Sgtb 		  krb5_creds *mcreds, krb5_creds *creds)
612505d05c7Sgtb {
613505d05c7Sgtb     return krb5_cc_retrieve_cred_default (context, id, whichfields,
614505d05c7Sgtb 					  mcreds, creds);
615505d05c7Sgtb }
616505d05c7Sgtb 
617*55fea89dSDan Cross /*
618505d05c7Sgtb  * Non-functional stub implementation for krb5_mcc_remove
619*55fea89dSDan Cross  *
620505d05c7Sgtb  * Errors:
621505d05c7Sgtb  *    KRB5_CC_NOSUPP - not implemented
622505d05c7Sgtb  */
623505d05c7Sgtb static krb5_error_code KRB5_CALLCONV
krb5_mcc_remove_cred(krb5_context context,krb5_ccache cache,krb5_flags flags,krb5_creds * creds)624505d05c7Sgtb krb5_mcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
625505d05c7Sgtb                      krb5_creds *creds)
626505d05c7Sgtb {
627505d05c7Sgtb     return KRB5_CC_NOSUPP;
628505d05c7Sgtb }
629505d05c7Sgtb 
630505d05c7Sgtb 
631505d05c7Sgtb /*
632505d05c7Sgtb  * Requires:
633505d05c7Sgtb  * id is a cred cache returned by krb5_mcc_resolve or
634505d05c7Sgtb  * krb5_mcc_generate_new, but has not been opened by krb5_mcc_initialize.
635505d05c7Sgtb  *
636505d05c7Sgtb  * Modifies:
637505d05c7Sgtb  * id
638*55fea89dSDan Cross  *
639505d05c7Sgtb  * Effects:
640505d05c7Sgtb  * Sets the operational flags of id to flags.
641505d05c7Sgtb  */
642505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_set_flags(krb5_context context,krb5_ccache id,krb5_flags flags)643505d05c7Sgtb krb5_mcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
644505d05c7Sgtb {
645505d05c7Sgtb     return KRB5_OK;
646505d05c7Sgtb }
647505d05c7Sgtb 
648159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV
krb5_mcc_get_flags(krb5_context context,krb5_ccache id,krb5_flags * flags)649159d09a2SMark Phalan krb5_mcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
650159d09a2SMark Phalan {
651159d09a2SMark Phalan     *flags = 0;
652159d09a2SMark Phalan     return KRB5_OK;
653159d09a2SMark Phalan }
654159d09a2SMark Phalan 
655505d05c7Sgtb /* store: Save away creds in the ccache.  */
656505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_mcc_store(krb5_context ctx,krb5_ccache id,krb5_creds * creds)657505d05c7Sgtb krb5_mcc_store(krb5_context ctx, krb5_ccache id, krb5_creds *creds)
658505d05c7Sgtb {
659505d05c7Sgtb     krb5_error_code err;
660505d05c7Sgtb     krb5_mcc_link *new_node;
661505d05c7Sgtb     krb5_mcc_data *mptr = (krb5_mcc_data *)id->data;
662505d05c7Sgtb 
663505d05c7Sgtb     new_node = malloc(sizeof(krb5_mcc_link));
664505d05c7Sgtb     if (new_node == NULL)
665505d05c7Sgtb 	return errno;
666505d05c7Sgtb     err = krb5_copy_creds(ctx, creds, &new_node->creds);
667505d05c7Sgtb     if (err) {
668505d05c7Sgtb 	free(new_node);
669505d05c7Sgtb 	return err;
670505d05c7Sgtb     }
671505d05c7Sgtb     err = k5_mutex_lock(&mptr->lock);
672505d05c7Sgtb     if (err) {
673159d09a2SMark Phalan         /* Solaris Kerberos - fix mem leaks */
674159d09a2SMark Phalan 	krb5_free_creds(ctx, new_node->creds);
675505d05c7Sgtb 	free(new_node);
676505d05c7Sgtb 	return err;
677505d05c7Sgtb     }
678505d05c7Sgtb     new_node->next = mptr->link;
679505d05c7Sgtb     mptr->link = new_node;
680505d05c7Sgtb     k5_mutex_unlock(&mptr->lock);
681505d05c7Sgtb     return 0;
682505d05c7Sgtb }
683505d05c7Sgtb 
684159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV
krb5_mcc_ptcursor_new(krb5_context context,krb5_cc_ptcursor * cursor)685159d09a2SMark Phalan krb5_mcc_ptcursor_new(
686159d09a2SMark Phalan     krb5_context context,
687159d09a2SMark Phalan     krb5_cc_ptcursor *cursor)
688159d09a2SMark Phalan {
689159d09a2SMark Phalan     krb5_error_code ret = 0;
690159d09a2SMark Phalan     krb5_cc_ptcursor n = NULL;
691159d09a2SMark Phalan     struct krb5_mcc_ptcursor_data *cdata = NULL;
692159d09a2SMark Phalan 
693159d09a2SMark Phalan     *cursor = NULL;
694159d09a2SMark Phalan 
695159d09a2SMark Phalan     n = malloc(sizeof(*n));
696159d09a2SMark Phalan     if (n == NULL)
697159d09a2SMark Phalan 	return ENOMEM;
698159d09a2SMark Phalan     n->ops = &krb5_mcc_ops;
699159d09a2SMark Phalan     cdata = malloc(sizeof(struct krb5_mcc_ptcursor_data));
700159d09a2SMark Phalan     if (cdata == NULL) {
701159d09a2SMark Phalan 	ret = ENOMEM;
702159d09a2SMark Phalan 	goto errout;
703159d09a2SMark Phalan     }
704159d09a2SMark Phalan     n->data = cdata;
705159d09a2SMark Phalan     ret = k5_mutex_lock(&krb5int_mcc_mutex);
706159d09a2SMark Phalan     if (ret)
707159d09a2SMark Phalan 	goto errout;
708159d09a2SMark Phalan     cdata->cur = mcc_head;
709159d09a2SMark Phalan     ret = k5_mutex_unlock(&krb5int_mcc_mutex);
710159d09a2SMark Phalan     if (ret)
711159d09a2SMark Phalan 	goto errout;
712159d09a2SMark Phalan 
713159d09a2SMark Phalan errout:
714159d09a2SMark Phalan     if (ret) {
715159d09a2SMark Phalan 	krb5_mcc_ptcursor_free(context, &n);
716159d09a2SMark Phalan     }
717159d09a2SMark Phalan     *cursor = n;
718159d09a2SMark Phalan     return ret;
719159d09a2SMark Phalan }
720159d09a2SMark Phalan 
721159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV
krb5_mcc_ptcursor_next(krb5_context context,krb5_cc_ptcursor cursor,krb5_ccache * ccache)722159d09a2SMark Phalan krb5_mcc_ptcursor_next(
723159d09a2SMark Phalan     krb5_context context,
724159d09a2SMark Phalan     krb5_cc_ptcursor cursor,
725159d09a2SMark Phalan     krb5_ccache *ccache)
726159d09a2SMark Phalan {
727159d09a2SMark Phalan     krb5_error_code ret = 0;
728159d09a2SMark Phalan     struct krb5_mcc_ptcursor_data *cdata = NULL;
729159d09a2SMark Phalan 
730159d09a2SMark Phalan     *ccache = NULL;
731159d09a2SMark Phalan     cdata = cursor->data;
732159d09a2SMark Phalan     if (cdata->cur == NULL)
733159d09a2SMark Phalan 	return 0;
734159d09a2SMark Phalan 
735159d09a2SMark Phalan     *ccache = malloc(sizeof(**ccache));
736159d09a2SMark Phalan     if (*ccache == NULL)
737159d09a2SMark Phalan 	return ENOMEM;
738159d09a2SMark Phalan 
739159d09a2SMark Phalan     (*ccache)->ops = &krb5_mcc_ops;
740159d09a2SMark Phalan     (*ccache)->data = cdata->cur->cache;
741159d09a2SMark Phalan     ret = k5_mutex_lock(&krb5int_mcc_mutex);
742159d09a2SMark Phalan     if (ret)
743159d09a2SMark Phalan 	goto errout;
744159d09a2SMark Phalan     cdata->cur = cdata->cur->next;
745159d09a2SMark Phalan     ret = k5_mutex_unlock(&krb5int_mcc_mutex);
746159d09a2SMark Phalan     if (ret)
747159d09a2SMark Phalan 	goto errout;
748159d09a2SMark Phalan errout:
749159d09a2SMark Phalan     if (ret && *ccache != NULL) {
750159d09a2SMark Phalan 	free(*ccache);
751159d09a2SMark Phalan 	*ccache = NULL;
752159d09a2SMark Phalan     }
753159d09a2SMark Phalan     return ret;
754159d09a2SMark Phalan }
755159d09a2SMark Phalan 
756159d09a2SMark Phalan static krb5_error_code KRB5_CALLCONV
krb5_mcc_ptcursor_free(krb5_context context,krb5_cc_ptcursor * cursor)757159d09a2SMark Phalan krb5_mcc_ptcursor_free(
758159d09a2SMark Phalan     krb5_context context,
759159d09a2SMark Phalan     krb5_cc_ptcursor *cursor)
760159d09a2SMark Phalan {
761159d09a2SMark Phalan     if (*cursor == NULL)
762159d09a2SMark Phalan 	return 0;
763159d09a2SMark Phalan     if ((*cursor)->data != NULL)
764159d09a2SMark Phalan 	free((*cursor)->data);
765159d09a2SMark Phalan     free(*cursor);
766159d09a2SMark Phalan     *cursor = NULL;
767159d09a2SMark Phalan     return 0;
768159d09a2SMark Phalan }
769159d09a2SMark Phalan 
770505d05c7Sgtb const krb5_cc_ops krb5_mcc_ops = {
771505d05c7Sgtb      0,
772505d05c7Sgtb      "MEMORY",
773505d05c7Sgtb      krb5_mcc_get_name,
774505d05c7Sgtb      krb5_mcc_resolve,
775505d05c7Sgtb      krb5_mcc_generate_new,
776505d05c7Sgtb      krb5_mcc_initialize,
777505d05c7Sgtb      krb5_mcc_destroy,
778505d05c7Sgtb      krb5_mcc_close,
779505d05c7Sgtb      krb5_mcc_store,
780505d05c7Sgtb      krb5_mcc_retrieve,
781505d05c7Sgtb      krb5_mcc_get_principal,
782505d05c7Sgtb      krb5_mcc_start_seq_get,
783505d05c7Sgtb      krb5_mcc_next_cred,
784505d05c7Sgtb      krb5_mcc_end_seq_get,
785505d05c7Sgtb      krb5_mcc_remove_cred,
786505d05c7Sgtb      krb5_mcc_set_flags,
787159d09a2SMark Phalan      krb5_mcc_get_flags,
788159d09a2SMark Phalan      krb5_mcc_ptcursor_new,
789159d09a2SMark Phalan      krb5_mcc_ptcursor_next,
790159d09a2SMark Phalan      krb5_mcc_ptcursor_free,
791159d09a2SMark Phalan      NULL,
792159d09a2SMark Phalan      NULL,
793159d09a2SMark Phalan      NULL,
794505d05c7Sgtb };
795