Line data Source code
1 : /*
2 : * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include "krb5_locl.h"
37 :
38 : typedef struct krb5_mcache {
39 : char *name;
40 : unsigned int refcnt;
41 : int dead;
42 : krb5_principal primary_principal;
43 : struct link {
44 : krb5_creds cred;
45 : struct link *next;
46 : } *creds;
47 : struct krb5_mcache *next;
48 : time_t mtime;
49 : krb5_deltat kdc_offset;
50 : } krb5_mcache;
51 :
52 : static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
53 : static struct krb5_mcache *mcc_head;
54 :
55 : #define MCACHE(X) ((krb5_mcache *)(X)->data.data)
56 :
57 : #define MISDEAD(X) ((X)->dead)
58 :
59 : static const char* KRB5_CALLCONV
60 145505 : mcc_get_name(krb5_context context,
61 : krb5_ccache id)
62 : {
63 145505 : return MCACHE(id)->name;
64 : }
65 :
66 : static krb5_mcache * KRB5_CALLCONV
67 147007 : mcc_alloc(const char *name)
68 : {
69 : krb5_mcache *m, *m_c;
70 147007 : int ret = 0;
71 :
72 147007 : ALLOC(m, 1);
73 147007 : if(m == NULL)
74 0 : return NULL;
75 147007 : if(name == NULL)
76 65242 : ret = asprintf(&m->name, "%p", m);
77 : else
78 82503 : m->name = strdup(name);
79 147745 : if(ret < 0 || m->name == NULL) {
80 0 : free(m);
81 0 : return NULL;
82 : }
83 : /* check for dups first */
84 : HEIMDAL_MUTEX_lock(&mcc_mutex);
85 1337873 : for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
86 1190866 : if (strcmp(m->name, m_c->name) == 0)
87 0 : break;
88 147007 : if (m_c) {
89 0 : free(m->name);
90 0 : free(m);
91 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
92 0 : return NULL;
93 : }
94 :
95 147007 : m->dead = 0;
96 147007 : m->refcnt = 1;
97 147007 : m->primary_principal = NULL;
98 147007 : m->creds = NULL;
99 147007 : m->mtime = time(NULL);
100 147007 : m->kdc_offset = 0;
101 147007 : m->next = mcc_head;
102 147007 : mcc_head = m;
103 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
104 147007 : return m;
105 : }
106 :
107 : static krb5_error_code KRB5_CALLCONV
108 244798 : mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
109 : {
110 : krb5_mcache *m;
111 :
112 : HEIMDAL_MUTEX_lock(&mcc_mutex);
113 1097636 : for (m = mcc_head; m != NULL; m = m->next)
114 1015133 : if (strcmp(m->name, res) == 0)
115 160229 : break;
116 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
117 :
118 244798 : if (m != NULL) {
119 162295 : m->refcnt++;
120 162295 : (*id)->data.data = m;
121 162295 : (*id)->data.length = sizeof(*m);
122 162295 : return 0;
123 : }
124 :
125 82503 : m = mcc_alloc(res);
126 82503 : if (m == NULL) {
127 0 : krb5_set_error_message(context, KRB5_CC_NOMEM,
128 0 : N_("malloc: out of memory", ""));
129 0 : return KRB5_CC_NOMEM;
130 : }
131 :
132 82503 : (*id)->data.data = m;
133 82503 : (*id)->data.length = sizeof(*m);
134 :
135 82503 : return 0;
136 : }
137 :
138 :
139 : static krb5_error_code KRB5_CALLCONV
140 64504 : mcc_gen_new(krb5_context context, krb5_ccache *id)
141 : {
142 : krb5_mcache *m;
143 :
144 64504 : m = mcc_alloc(NULL);
145 :
146 64504 : if (m == NULL) {
147 0 : krb5_set_error_message(context, KRB5_CC_NOMEM,
148 0 : N_("malloc: out of memory", ""));
149 0 : return KRB5_CC_NOMEM;
150 : }
151 :
152 64504 : (*id)->data.data = m;
153 64504 : (*id)->data.length = sizeof(*m);
154 :
155 64504 : return 0;
156 : }
157 :
158 : static void KRB5_CALLCONV
159 280872 : mcc_destroy_internal(krb5_context context,
160 : krb5_mcache *m)
161 : {
162 : struct link *l;
163 :
164 280872 : if (m->primary_principal != NULL) {
165 139255 : krb5_free_principal (context, m->primary_principal);
166 139255 : m->primary_principal = NULL;
167 : }
168 280872 : m->dead = 1;
169 :
170 280872 : l = m->creds;
171 712238 : while (l != NULL) {
172 : struct link *old;
173 :
174 150494 : krb5_free_cred_contents (context, &l->cred);
175 150494 : old = l;
176 150494 : l = l->next;
177 150494 : free (old);
178 : }
179 :
180 280872 : m->creds = NULL;
181 280872 : return;
182 : }
183 :
184 : static krb5_error_code KRB5_CALLCONV
185 141514 : mcc_initialize(krb5_context context,
186 : krb5_ccache id,
187 : krb5_principal primary_principal)
188 : {
189 141514 : krb5_mcache *m = MCACHE(id);
190 : /*
191 : * It's important to destroy any existing
192 : * creds here, that matches the baheviour
193 : * of all other backends and also the
194 : * MEMORY: backend in MIT.
195 : */
196 141514 : mcc_destroy_internal(context, m);
197 141514 : m->dead = 0;
198 141514 : m->kdc_offset = 0;
199 141514 : m->mtime = time(NULL);
200 141514 : return krb5_copy_principal (context,
201 : primary_principal,
202 : &m->primary_principal);
203 : }
204 :
205 : static int
206 303559 : mcc_close_internal(krb5_mcache *m)
207 : {
208 307389 : if (--m->refcnt != 0)
209 144662 : return 0;
210 :
211 160662 : if (MISDEAD(m)) {
212 139327 : free (m->name);
213 137562 : return 1;
214 : }
215 21335 : return 0;
216 : }
217 :
218 : static krb5_error_code KRB5_CALLCONV
219 307389 : mcc_close(krb5_context context,
220 : krb5_ccache id)
221 : {
222 309154 : if (mcc_close_internal(MCACHE(id)))
223 139327 : krb5_data_free(&id->data);
224 307389 : return 0;
225 : }
226 :
227 : static krb5_error_code KRB5_CALLCONV
228 139358 : mcc_destroy(krb5_context context,
229 : krb5_ccache id)
230 : {
231 139358 : krb5_mcache **n, *m = MCACHE(id);
232 :
233 139358 : if (m->refcnt == 0)
234 0 : krb5_abortx(context, "mcc_destroy: refcnt already 0");
235 :
236 139358 : if (!MISDEAD(m)) {
237 : /* if this is an active mcache, remove it from the linked
238 : list, and free all data */
239 : HEIMDAL_MUTEX_lock(&mcc_mutex);
240 271709 : for(n = &mcc_head; n && *n; n = &(*n)->next) {
241 273479 : if(m == *n) {
242 139358 : *n = m->next;
243 139358 : break;
244 : }
245 : }
246 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
247 139358 : mcc_destroy_internal(context, m);
248 : }
249 139358 : return 0;
250 : }
251 :
252 : static krb5_error_code KRB5_CALLCONV
253 160909 : mcc_store_cred(krb5_context context,
254 : krb5_ccache id,
255 : krb5_creds *creds)
256 : {
257 160909 : krb5_mcache *m = MCACHE(id);
258 : krb5_error_code ret;
259 : struct link *l;
260 :
261 160909 : if (MISDEAD(m))
262 6 : return ENOENT;
263 :
264 160903 : l = malloc (sizeof(*l));
265 160903 : if (l == NULL) {
266 0 : krb5_set_error_message(context, KRB5_CC_NOMEM,
267 0 : N_("malloc: out of memory", ""));
268 0 : return KRB5_CC_NOMEM;
269 : }
270 160903 : l->next = m->creds;
271 160903 : m->creds = l;
272 163013 : memset (&l->cred, 0, sizeof(l->cred));
273 160903 : ret = krb5_copy_creds_contents (context, creds, &l->cred);
274 160903 : if (ret) {
275 0 : m->creds = l->next;
276 0 : free (l);
277 0 : return ret;
278 : }
279 160903 : m->mtime = time(NULL);
280 160903 : return 0;
281 : }
282 :
283 : static krb5_error_code KRB5_CALLCONV
284 352461 : mcc_get_principal(krb5_context context,
285 : krb5_ccache id,
286 : krb5_principal *principal)
287 : {
288 352461 : krb5_mcache *m = MCACHE(id);
289 :
290 352461 : if (MISDEAD(m) || m->primary_principal == NULL)
291 15843 : return ENOENT;
292 336618 : return krb5_copy_principal (context,
293 330058 : m->primary_principal,
294 : principal);
295 : }
296 :
297 : static krb5_error_code KRB5_CALLCONV
298 280727 : mcc_get_first (krb5_context context,
299 : krb5_ccache id,
300 : krb5_cc_cursor *cursor)
301 : {
302 280727 : krb5_mcache *m = MCACHE(id);
303 :
304 280727 : if (MISDEAD(m))
305 0 : return ENOENT;
306 :
307 280727 : *cursor = m->creds;
308 280727 : return 0;
309 : }
310 :
311 : static krb5_error_code KRB5_CALLCONV
312 677052 : mcc_get_next (krb5_context context,
313 : krb5_ccache id,
314 : krb5_cc_cursor *cursor,
315 : krb5_creds *creds)
316 : {
317 677052 : krb5_mcache *m = MCACHE(id);
318 : struct link *l;
319 :
320 677052 : if (MISDEAD(m))
321 0 : return ENOENT;
322 :
323 677052 : l = *cursor;
324 677052 : if (l != NULL) {
325 527261 : *cursor = l->next;
326 527261 : return krb5_copy_creds_contents (context,
327 527261 : &l->cred,
328 : creds);
329 : } else
330 146875 : return KRB5_CC_END;
331 : }
332 :
333 : static krb5_error_code KRB5_CALLCONV
334 280727 : mcc_end_get (krb5_context context,
335 : krb5_ccache id,
336 : krb5_cc_cursor *cursor)
337 : {
338 280727 : return 0;
339 : }
340 :
341 : static krb5_error_code KRB5_CALLCONV
342 605 : mcc_remove_cred(krb5_context context,
343 : krb5_ccache id,
344 : krb5_flags which,
345 : krb5_creds *mcreds)
346 : {
347 605 : krb5_mcache *m = MCACHE(id);
348 : struct link **q, *p;
349 1828 : for(q = &m->creds, p = *q; p; p = *q) {
350 1223 : if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
351 596 : *q = p->next;
352 596 : krb5_free_cred_contents(context, &p->cred);
353 596 : free(p);
354 596 : m->mtime = time(NULL);
355 : } else
356 627 : q = &p->next;
357 : }
358 605 : return 0;
359 : }
360 :
361 : static krb5_error_code KRB5_CALLCONV
362 0 : mcc_set_flags(krb5_context context,
363 : krb5_ccache id,
364 : krb5_flags flags)
365 : {
366 0 : return 0; /* XXX */
367 : }
368 :
369 : struct mcache_iter {
370 : krb5_mcache *cache;
371 : };
372 :
373 : static krb5_error_code KRB5_CALLCONV
374 1 : mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
375 : {
376 : struct mcache_iter *iter;
377 :
378 1 : iter = calloc(1, sizeof(*iter));
379 1 : if (iter == NULL) {
380 0 : krb5_set_error_message(context, ENOMEM,
381 0 : N_("malloc: out of memory", ""));
382 0 : return ENOMEM;
383 : }
384 :
385 : HEIMDAL_MUTEX_lock(&mcc_mutex);
386 1 : iter->cache = mcc_head;
387 1 : if (iter->cache)
388 0 : iter->cache->refcnt++;
389 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
390 :
391 1 : *cursor = iter;
392 1 : return 0;
393 : }
394 :
395 : static krb5_error_code KRB5_CALLCONV
396 1 : mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
397 : {
398 1 : struct mcache_iter *iter = cursor;
399 : krb5_error_code ret;
400 : krb5_mcache *m;
401 :
402 1 : if (iter->cache == NULL)
403 1 : return KRB5_CC_END;
404 :
405 : HEIMDAL_MUTEX_lock(&mcc_mutex);
406 0 : m = iter->cache;
407 0 : if (m->next)
408 0 : m->next->refcnt++;
409 0 : iter->cache = m->next;
410 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
411 :
412 0 : ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
413 0 : if (ret)
414 0 : return ret;
415 :
416 0 : (*id)->data.data = m;
417 0 : (*id)->data.length = sizeof(*m);
418 :
419 0 : return 0;
420 : }
421 :
422 : static krb5_error_code KRB5_CALLCONV
423 1 : mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
424 : {
425 1 : struct mcache_iter *iter = cursor;
426 :
427 1 : if (iter->cache)
428 0 : mcc_close_internal(iter->cache);
429 1 : iter->cache = NULL;
430 1 : free(iter);
431 1 : return 0;
432 : }
433 :
434 : static krb5_error_code KRB5_CALLCONV
435 0 : mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
436 : {
437 0 : krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
438 : struct link *creds;
439 : krb5_principal principal;
440 : krb5_mcache **n;
441 :
442 : HEIMDAL_MUTEX_lock(&mcc_mutex);
443 :
444 : /* drop the from cache from the linked list to avoid lookups */
445 0 : for(n = &mcc_head; n && *n; n = &(*n)->next) {
446 0 : if(mfrom == *n) {
447 0 : *n = mfrom->next;
448 0 : break;
449 : }
450 : }
451 :
452 : /* swap creds */
453 0 : creds = mto->creds;
454 0 : mto->creds = mfrom->creds;
455 0 : mfrom->creds = creds;
456 : /* swap principal */
457 0 : principal = mto->primary_principal;
458 0 : mto->primary_principal = mfrom->primary_principal;
459 0 : mfrom->primary_principal = principal;
460 :
461 0 : mto->mtime = mfrom->mtime = time(NULL);
462 :
463 : HEIMDAL_MUTEX_unlock(&mcc_mutex);
464 0 : mcc_destroy(context, from);
465 :
466 0 : return 0;
467 : }
468 :
469 : static krb5_error_code KRB5_CALLCONV
470 0 : mcc_default_name(krb5_context context, char **str)
471 : {
472 0 : *str = strdup("MEMORY:");
473 0 : if (*str == NULL) {
474 0 : krb5_set_error_message(context, ENOMEM,
475 0 : N_("malloc: out of memory", ""));
476 0 : return ENOMEM;
477 : }
478 0 : return 0;
479 : }
480 :
481 : static krb5_error_code KRB5_CALLCONV
482 0 : mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
483 : {
484 0 : *mtime = MCACHE(id)->mtime;
485 0 : return 0;
486 : }
487 :
488 : static krb5_error_code KRB5_CALLCONV
489 0 : mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
490 : {
491 0 : krb5_mcache *m = MCACHE(id);
492 0 : m->kdc_offset = kdc_offset;
493 0 : return 0;
494 : }
495 :
496 : static krb5_error_code KRB5_CALLCONV
497 20868 : mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
498 : {
499 20868 : krb5_mcache *m = MCACHE(id);
500 20868 : *kdc_offset = m->kdc_offset;
501 20868 : return 0;
502 : }
503 :
504 :
505 : /**
506 : * Variable containing the MEMORY based credential cache implemention.
507 : *
508 : * @ingroup krb5_ccache
509 : */
510 :
511 : KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
512 : KRB5_CC_OPS_VERSION,
513 : "MEMORY",
514 : mcc_get_name,
515 : mcc_resolve,
516 : mcc_gen_new,
517 : mcc_initialize,
518 : mcc_destroy,
519 : mcc_close,
520 : mcc_store_cred,
521 : NULL, /* mcc_retrieve */
522 : mcc_get_principal,
523 : mcc_get_first,
524 : mcc_get_next,
525 : mcc_end_get,
526 : mcc_remove_cred,
527 : mcc_set_flags,
528 : NULL,
529 : mcc_get_cache_first,
530 : mcc_get_cache_next,
531 : mcc_end_cache_get,
532 : mcc_move,
533 : mcc_default_name,
534 : NULL,
535 : mcc_lastchange,
536 : mcc_set_kdc_offset,
537 : mcc_get_kdc_offset
538 : };
|