]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/kcm/cache.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / kcm / cache.c
1 /*
2  * Copyright (c) 2005, PADL Software Pty Ltd.
3  * All rights reserved.
4  *
5  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of PADL Software nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "kcm_locl.h"
36
37 HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER;
38 kcm_ccache_data *ccache_head = NULL;
39 static unsigned int ccache_nextid = 0;
40
41 char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid)
42 {
43     unsigned n;
44     char *name;
45
46     HEIMDAL_MUTEX_lock(&ccache_mutex);
47     n = ++ccache_nextid;
48     HEIMDAL_MUTEX_unlock(&ccache_mutex);
49
50     asprintf(&name, "%ld:%u", (long)uid, n);
51
52     return name;
53 }
54
55 krb5_error_code
56 kcm_ccache_resolve(krb5_context context,
57                    const char *name,
58                    kcm_ccache *ccache)
59 {
60     kcm_ccache p;
61     krb5_error_code ret;
62
63     *ccache = NULL;
64
65     ret = KRB5_FCC_NOFILE;
66
67     HEIMDAL_MUTEX_lock(&ccache_mutex);
68
69     for (p = ccache_head; p != NULL; p = p->next) {
70         if ((p->flags & KCM_FLAGS_VALID) == 0)
71             continue;
72         if (strcmp(p->name, name) == 0) {
73             ret = 0;
74             break;
75         }
76     }
77
78     if (ret == 0) {
79         kcm_retain_ccache(context, p);
80         *ccache = p;
81     }
82
83     HEIMDAL_MUTEX_unlock(&ccache_mutex);
84
85     return ret;
86 }
87
88 krb5_error_code
89 kcm_ccache_resolve_by_uuid(krb5_context context,
90                            kcmuuid_t uuid,
91                            kcm_ccache *ccache)
92 {
93     kcm_ccache p;
94     krb5_error_code ret;
95
96     *ccache = NULL;
97
98     ret = KRB5_FCC_NOFILE;
99
100     HEIMDAL_MUTEX_lock(&ccache_mutex);
101
102     for (p = ccache_head; p != NULL; p = p->next) {
103         if ((p->flags & KCM_FLAGS_VALID) == 0)
104             continue;
105         if (memcmp(p->uuid, uuid, sizeof(kcmuuid_t)) == 0) {
106             ret = 0;
107             break;
108         }
109     }
110
111     if (ret == 0) {
112         kcm_retain_ccache(context, p);
113         *ccache = p;
114     }
115
116     HEIMDAL_MUTEX_unlock(&ccache_mutex);
117
118     return ret;
119 }
120
121 krb5_error_code
122 kcm_ccache_get_uuids(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *sp)
123 {
124     krb5_error_code ret;
125     kcm_ccache p;
126
127     ret = KRB5_FCC_NOFILE;
128
129     HEIMDAL_MUTEX_lock(&ccache_mutex);
130
131     for (p = ccache_head; p != NULL; p = p->next) {
132         if ((p->flags & KCM_FLAGS_VALID) == 0)
133             continue;
134         ret = kcm_access(context, client, opcode, p);
135         if (ret) {
136             ret = 0;
137             continue;
138         }
139         krb5_storage_write(sp, p->uuid, sizeof(p->uuid));
140     }
141
142     HEIMDAL_MUTEX_unlock(&ccache_mutex);
143
144     return ret;
145 }
146
147
148 krb5_error_code kcm_debug_ccache(krb5_context context)
149 {
150     kcm_ccache p;
151
152     for (p = ccache_head; p != NULL; p = p->next) {
153         char *cpn = NULL, *spn = NULL;
154         int ncreds = 0;
155         struct kcm_creds *k;
156
157         if ((p->flags & KCM_FLAGS_VALID) == 0) {
158             kcm_log(7, "cache %08x: empty slot");
159             continue;
160         }
161
162         KCM_ASSERT_VALID(p);
163
164         for (k = p->creds; k != NULL; k = k->next)
165             ncreds++;
166
167         if (p->client != NULL)
168             krb5_unparse_name(context, p->client, &cpn);
169         if (p->server != NULL)
170             krb5_unparse_name(context, p->server, &spn);
171
172         kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o "
173                 "uid %d gid %d client %s server %s ncreds %d",
174                 p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid,
175                 (cpn == NULL) ? "<none>" : cpn,
176                 (spn == NULL) ? "<none>" : spn,
177                 ncreds);
178
179         if (cpn != NULL)
180             free(cpn);
181         if (spn != NULL)
182             free(spn);
183     }
184
185     return 0;
186 }
187
188 static void
189 kcm_free_ccache_data_internal(krb5_context context,
190                               kcm_ccache_data *cache)
191 {
192     KCM_ASSERT_VALID(cache);
193
194     if (cache->name != NULL) {
195         free(cache->name);
196         cache->name = NULL;
197     }
198
199     if (cache->flags & KCM_FLAGS_USE_KEYTAB) {
200         krb5_kt_close(context, cache->key.keytab);
201         cache->key.keytab = NULL;
202     } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) {
203         krb5_free_keyblock_contents(context, &cache->key.keyblock);
204         krb5_keyblock_zero(&cache->key.keyblock);
205     }
206
207     cache->flags = 0;
208     cache->mode = 0;
209     cache->uid = -1;
210     cache->gid = -1;
211     cache->session = -1;
212
213     kcm_zero_ccache_data_internal(context, cache);
214
215     cache->tkt_life = 0;
216     cache->renew_life = 0;
217
218     cache->next = NULL;
219     cache->refcnt = 0;
220
221     HEIMDAL_MUTEX_unlock(&cache->mutex);
222     HEIMDAL_MUTEX_destroy(&cache->mutex);
223 }
224
225
226 krb5_error_code
227 kcm_ccache_destroy(krb5_context context, const char *name)
228 {
229     kcm_ccache *p, ccache;
230     krb5_error_code ret;
231
232     ret = KRB5_FCC_NOFILE;
233
234     HEIMDAL_MUTEX_lock(&ccache_mutex);
235     for (p = &ccache_head; *p != NULL; p = &(*p)->next) {
236         if (((*p)->flags & KCM_FLAGS_VALID) == 0)
237             continue;
238         if (strcmp((*p)->name, name) == 0) {
239             ret = 0;
240             break;
241         }
242     }
243     if (ret)
244         goto out;
245
246     if ((*p)->refcnt != 1) {
247         ret = EAGAIN;
248         goto out;
249     }
250
251     ccache = *p;
252     *p = (*p)->next;
253     kcm_free_ccache_data_internal(context, ccache);
254     free(ccache);
255
256 out:
257     HEIMDAL_MUTEX_unlock(&ccache_mutex);
258
259     return ret;
260 }
261
262 static krb5_error_code
263 kcm_ccache_alloc(krb5_context context,
264                  const char *name,
265                  kcm_ccache *ccache)
266 {
267     kcm_ccache slot = NULL, p;
268     krb5_error_code ret;
269     int new_slot = 0;
270
271     *ccache = NULL;
272
273     /* First, check for duplicates */
274     HEIMDAL_MUTEX_lock(&ccache_mutex);
275     ret = 0;
276     for (p = ccache_head; p != NULL; p = p->next) {
277         if (p->flags & KCM_FLAGS_VALID) {
278             if (strcmp(p->name, name) == 0) {
279                 ret = KRB5_CC_WRITE;
280                 break;
281             }
282         } else if (slot == NULL)
283             slot = p;
284     }
285
286     if (ret)
287         goto out;
288
289     /*
290      * Create an enpty slot for us.
291      */
292     if (slot == NULL) {
293         slot = (kcm_ccache_data *)malloc(sizeof(*slot));
294         if (slot == NULL) {
295             ret = KRB5_CC_NOMEM;
296             goto out;
297         }
298         slot->next = ccache_head;
299         HEIMDAL_MUTEX_init(&slot->mutex);
300         new_slot = 1;
301     }
302
303     RAND_bytes(slot->uuid, sizeof(slot->uuid));
304
305     slot->name = strdup(name);
306     if (slot->name == NULL) {
307         ret = KRB5_CC_NOMEM;
308         goto out;
309     }
310
311     slot->refcnt = 1;
312     slot->flags = KCM_FLAGS_VALID;
313     slot->mode = S_IRUSR | S_IWUSR;
314     slot->uid = -1;
315     slot->gid = -1;
316     slot->client = NULL;
317     slot->server = NULL;
318     slot->creds = NULL;
319     slot->key.keytab = NULL;
320     slot->tkt_life = 0;
321     slot->renew_life = 0;
322
323     if (new_slot)
324         ccache_head = slot;
325
326     *ccache = slot;
327
328     HEIMDAL_MUTEX_unlock(&ccache_mutex);
329     return 0;
330
331 out:
332     HEIMDAL_MUTEX_unlock(&ccache_mutex);
333     if (new_slot && slot != NULL) {
334         HEIMDAL_MUTEX_destroy(&slot->mutex);
335         free(slot);
336     }
337     return ret;
338 }
339
340 krb5_error_code
341 kcm_ccache_remove_creds_internal(krb5_context context,
342                                  kcm_ccache ccache)
343 {
344     struct kcm_creds *k;
345
346     k = ccache->creds;
347     while (k != NULL) {
348         struct kcm_creds *old;
349
350         krb5_free_cred_contents(context, &k->cred);
351         old = k;
352         k = k->next;
353         free(old);
354     }
355     ccache->creds = NULL;
356
357     return 0;
358 }
359
360 krb5_error_code
361 kcm_ccache_remove_creds(krb5_context context,
362                         kcm_ccache ccache)
363 {
364     krb5_error_code ret;
365
366     KCM_ASSERT_VALID(ccache);
367
368     HEIMDAL_MUTEX_lock(&ccache->mutex);
369     ret = kcm_ccache_remove_creds_internal(context, ccache);
370     HEIMDAL_MUTEX_unlock(&ccache->mutex);
371
372     return ret;
373 }
374
375 krb5_error_code
376 kcm_zero_ccache_data_internal(krb5_context context,
377                               kcm_ccache_data *cache)
378 {
379     if (cache->client != NULL) {
380         krb5_free_principal(context, cache->client);
381         cache->client = NULL;
382     }
383
384     if (cache->server != NULL) {
385         krb5_free_principal(context, cache->server);
386         cache->server = NULL;
387     }
388
389     kcm_ccache_remove_creds_internal(context, cache);
390
391     return 0;
392 }
393
394 krb5_error_code
395 kcm_zero_ccache_data(krb5_context context,
396                      kcm_ccache cache)
397 {
398     krb5_error_code ret;
399
400     KCM_ASSERT_VALID(cache);
401
402     HEIMDAL_MUTEX_lock(&cache->mutex);
403     ret = kcm_zero_ccache_data_internal(context, cache);
404     HEIMDAL_MUTEX_unlock(&cache->mutex);
405
406     return ret;
407 }
408
409 krb5_error_code
410 kcm_retain_ccache(krb5_context context,
411                   kcm_ccache ccache)
412 {
413     KCM_ASSERT_VALID(ccache);
414
415     HEIMDAL_MUTEX_lock(&ccache->mutex);
416     ccache->refcnt++;
417     HEIMDAL_MUTEX_unlock(&ccache->mutex);
418
419     return 0;
420 }
421
422 krb5_error_code
423 kcm_release_ccache(krb5_context context, kcm_ccache c)
424 {
425     krb5_error_code ret = 0;
426
427     KCM_ASSERT_VALID(c);
428
429     HEIMDAL_MUTEX_lock(&c->mutex);
430     if (c->refcnt == 1) {
431         kcm_free_ccache_data_internal(context, c);
432         free(c);
433     } else {
434         c->refcnt--;
435         HEIMDAL_MUTEX_unlock(&c->mutex);
436     }
437
438     return ret;
439 }
440
441 krb5_error_code
442 kcm_ccache_gen_new(krb5_context context,
443                    pid_t pid,
444                    uid_t uid,
445                    gid_t gid,
446                    kcm_ccache *ccache)
447 {
448     krb5_error_code ret;
449     char *name;
450
451     name = kcm_ccache_nextid(pid, uid, gid);
452     if (name == NULL) {
453         return KRB5_CC_NOMEM;
454     }
455
456     ret = kcm_ccache_new(context, name, ccache);
457
458     free(name);
459     return ret;
460 }
461
462 krb5_error_code
463 kcm_ccache_new(krb5_context context,
464                const char *name,
465                kcm_ccache *ccache)
466 {
467     krb5_error_code ret;
468
469     ret = kcm_ccache_alloc(context, name, ccache);
470     if (ret == 0) {
471         /*
472          * one reference is held by the linked list,
473          * one by the caller
474          */
475         kcm_retain_ccache(context, *ccache);
476     }
477
478     return ret;
479 }
480
481 krb5_error_code
482 kcm_ccache_destroy_if_empty(krb5_context context,
483                             kcm_ccache ccache)
484 {
485     krb5_error_code ret;
486
487     KCM_ASSERT_VALID(ccache);
488
489     if (ccache->creds == NULL) {
490         ret = kcm_ccache_destroy(context, ccache->name);
491     } else
492         ret = 0;
493
494     return ret;
495 }
496
497 krb5_error_code
498 kcm_ccache_store_cred(krb5_context context,
499                       kcm_ccache ccache,
500                       krb5_creds *creds,
501                       int copy)
502 {
503     krb5_error_code ret;
504     krb5_creds *tmp;
505
506     KCM_ASSERT_VALID(ccache);
507
508     HEIMDAL_MUTEX_lock(&ccache->mutex);
509     ret = kcm_ccache_store_cred_internal(context, ccache, creds, copy, &tmp);
510     HEIMDAL_MUTEX_unlock(&ccache->mutex);
511
512     return ret;
513 }
514
515 struct kcm_creds *
516 kcm_ccache_find_cred_uuid(krb5_context context,
517                           kcm_ccache ccache,
518                           kcmuuid_t uuid)
519 {
520     struct kcm_creds *c;
521
522     for (c = ccache->creds; c != NULL; c = c->next)
523         if (memcmp(c->uuid, uuid, sizeof(c->uuid)) == 0)
524             return c;
525
526     return NULL;
527 }
528
529
530
531 krb5_error_code
532 kcm_ccache_store_cred_internal(krb5_context context,
533                                kcm_ccache ccache,
534                                krb5_creds *creds,
535                                int copy,
536                                krb5_creds **credp)
537 {
538     struct kcm_creds **c;
539     krb5_error_code ret;
540
541     for (c = &ccache->creds; *c != NULL; c = &(*c)->next)
542         ;
543
544     *c = (struct kcm_creds *)calloc(1, sizeof(**c));
545     if (*c == NULL)
546         return KRB5_CC_NOMEM;
547
548     RAND_bytes((*c)->uuid, sizeof((*c)->uuid));
549
550     *credp = &(*c)->cred;
551
552     if (copy) {
553         ret = krb5_copy_creds_contents(context, creds, *credp);
554         if (ret) {
555             free(*c);
556             *c = NULL;
557         }
558     } else {
559         **credp = *creds;
560         ret = 0;
561     }
562
563     return ret;
564 }
565
566 krb5_error_code
567 kcm_ccache_remove_cred_internal(krb5_context context,
568                                 kcm_ccache ccache,
569                                 krb5_flags whichfields,
570                                 const krb5_creds *mcreds)
571 {
572     krb5_error_code ret;
573     struct kcm_creds **c;
574
575     ret = KRB5_CC_NOTFOUND;
576
577     for (c = &ccache->creds; *c != NULL; c = &(*c)->next) {
578         if (krb5_compare_creds(context, whichfields, mcreds, &(*c)->cred)) {
579             struct kcm_creds *cred = *c;
580
581             *c = cred->next;
582             krb5_free_cred_contents(context, &cred->cred);
583             free(cred);
584             ret = 0;
585             if (*c == NULL)
586                 break;
587         }
588     }
589
590     return ret;
591 }
592
593 krb5_error_code
594 kcm_ccache_remove_cred(krb5_context context,
595                        kcm_ccache ccache,
596                        krb5_flags whichfields,
597                        const krb5_creds *mcreds)
598 {
599     krb5_error_code ret;
600
601     KCM_ASSERT_VALID(ccache);
602
603     HEIMDAL_MUTEX_lock(&ccache->mutex);
604     ret = kcm_ccache_remove_cred_internal(context, ccache, whichfields, mcreds);
605     HEIMDAL_MUTEX_unlock(&ccache->mutex);
606
607     return ret;
608 }
609
610 krb5_error_code
611 kcm_ccache_retrieve_cred_internal(krb5_context context,
612                                   kcm_ccache ccache,
613                                   krb5_flags whichfields,
614                                   const krb5_creds *mcreds,
615                                   krb5_creds **creds)
616 {
617     krb5_boolean match;
618     struct kcm_creds *c;
619     krb5_error_code ret;
620
621     memset(creds, 0, sizeof(*creds));
622
623     ret = KRB5_CC_END;
624
625     match = FALSE;
626     for (c = ccache->creds; c != NULL; c = c->next) {
627         match = krb5_compare_creds(context, whichfields, mcreds, &c->cred);
628         if (match)
629             break;
630     }
631
632     if (match) {
633         ret = 0;
634         *creds = &c->cred;
635     }
636
637     return ret;
638 }
639
640 krb5_error_code
641 kcm_ccache_retrieve_cred(krb5_context context,
642                          kcm_ccache ccache,
643                          krb5_flags whichfields,
644                          const krb5_creds *mcreds,
645                          krb5_creds **credp)
646 {
647     krb5_error_code ret;
648
649     KCM_ASSERT_VALID(ccache);
650
651     HEIMDAL_MUTEX_lock(&ccache->mutex);
652     ret = kcm_ccache_retrieve_cred_internal(context, ccache,
653                                             whichfields, mcreds, credp);
654     HEIMDAL_MUTEX_unlock(&ccache->mutex);
655
656     return ret;
657 }
658
659 char *
660 kcm_ccache_first_name(kcm_client *client)
661 {
662     kcm_ccache p;
663     char *name = NULL;
664
665     HEIMDAL_MUTEX_lock(&ccache_mutex);
666
667     for (p = ccache_head; p != NULL; p = p->next) {
668         if (kcm_is_same_session(client, p->uid, p->session))
669             break;
670     }
671     if (p)
672         name = strdup(p->name);
673     HEIMDAL_MUTEX_unlock(&ccache_mutex);
674     return name;
675 }