lib/krb5: allow access to anonymous mcache entries via name
authorStefan Metzmacher <metze@samba.org>
Wed, 1 Apr 2020 21:09:57 +0000 (23:09 +0200)
committerJoseph Sutton <josephsutton@catalyst.net.nz>
Wed, 3 May 2023 04:13:16 +0000 (16:13 +1200)
The idea of anonymous mcache entries is that they won't be
included in the global ccache collection. But at the
same time they should be accessable via a name.

There might be better ways to do this, e.g. let the
caller specify a name like 'anonymous-application-key1'.

But we need a way to use MEMORY ccaches for different
security contexts, without the fear that they are randomly
used from the global list.

The better way would have been to opt-in in order to
fill the global ccache collection.

See 7e858c51b690ff0322766b328f60b41bc38d4ae3 for (at least part)
of the mess... there should not be a single global ccache collection
for MEMORY: ccaches! That is a security problem for applications
which used to be able to switch between different MEMORY ccaches!

Signed-off-by: Stefan Metzmacher <metze@samba.org>
lib/krb5/mcache.c

index b381cae8008d026ffcd8fc8549451e154b3bb271..fdd5674c3b87f980f5f35c8af579557f42d67a1f 100644 (file)
@@ -80,6 +80,7 @@ mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
     krb5_mcache *m, *m_c;
     size_t counter = 0;
     int ret = 0;
+    unsigned create_anonymous = 0;
 
     *out = NULL;
     ALLOC(m, 1);
@@ -92,27 +93,18 @@ again:
         free(m);
         return EAGAIN; /* XXX */
     }
-    if(name == NULL)
+    if(name == NULL) {
        ret = asprintf(&m->name, "u%p-%llu", m, (unsigned long long)counter);
-    else
+    } else if (strcmp(name, "anonymous") == 0) {
+       ret = asprintf(&m->name, "anonymous-%p-%llu", m, (unsigned long long)counter);
+       create_anonymous = 1;
+    } else {
        m->name = strdup(name);
+    }
     if(ret < 0 || m->name == NULL) {
        free(m);
        return krb5_enomem(context);
     }
-    if (strcmp(m->name, "anonymous") == 0) {
-        HEIMDAL_MUTEX_init(&(m->mutex));
-        m->anonymous = 1;
-        m->dead = 0;
-        m->refcnt = 1;
-        m->primary_principal = NULL;
-        m->creds = NULL;
-        m->mtime = time(NULL);
-        m->kdc_offset = 0;
-        m->next = NULL;
-        *out = m;
-        return 0;
-    }
 
     /* check for dups first */
     HEIMDAL_MUTEX_lock(&mcc_mutex);
@@ -120,7 +112,7 @@ again:
         if (strcmp(m->name, m_c->name) == 0)
             break;
     if (m_c) {
-        if (name) {
+        if (name && !create_anonymous) {
             /* We raced with another thread to create this cache */
             free(m->name);
             free(m);
@@ -141,7 +133,7 @@ again:
         return 0;
     }
 
-    m->anonymous = 0;
+    m->anonymous = create_anonymous;
     m->dead = 0;
     m->refcnt = 1;
     m->primary_principal = NULL;
@@ -279,18 +271,6 @@ mcc_destroy(krb5_context context,
 {
     krb5_mcache **n, *m = MCACHE(id);
 
-    if (m->anonymous) {
-        HEIMDAL_MUTEX_lock(&(m->mutex));
-        if (m->refcnt == 0) {
-            HEIMDAL_MUTEX_unlock(&(m->mutex));
-            krb5_abortx(context, "mcc_destroy: refcnt already 0");
-        }
-        if (!MISDEAD(m))
-            mcc_destroy_internal(context, m);
-        HEIMDAL_MUTEX_unlock(&(m->mutex));
-        return 0;
-    }
-
     HEIMDAL_MUTEX_lock(&mcc_mutex);
     HEIMDAL_MUTEX_lock(&(m->mutex));
     if (m->refcnt == 0)
@@ -458,6 +438,24 @@ struct mcache_iter {
     krb5_mcache *cache;
 };
 
+static krb5_mcache *
+mcc_get_cache_find_next_internal(krb5_mcache *next)
+{
+    HEIMDAL_MUTEX_lock(&mcc_mutex);
+    for (; next != NULL && next->anonymous; next = next->next) {
+       /* noop: iterate over all anonymous entries */
+    }
+    if (next != NULL) {
+       HEIMDAL_MUTEX_lock(&(next->mutex));
+       next->refcnt++;
+       HEIMDAL_MUTEX_unlock(&(next->mutex));
+       next = next->next;
+    }
+    HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+    return next;
+}
+
 static krb5_error_code KRB5_CALLCONV
 mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
 {
@@ -467,14 +465,7 @@ mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
     if (iter == NULL)
        return krb5_enomem(context);
 
-    HEIMDAL_MUTEX_lock(&mcc_mutex);
-    iter->cache = mcc_head;
-    if (iter->cache) {
-       HEIMDAL_MUTEX_lock(&(iter->cache->mutex));
-       iter->cache->refcnt++;
-       HEIMDAL_MUTEX_unlock(&(iter->cache->mutex));
-    }
-    HEIMDAL_MUTEX_unlock(&mcc_mutex);
+    iter->cache = mcc_get_cache_find_next_internal(mcc_head);
 
     *cursor = iter;
     return 0;
@@ -490,17 +481,8 @@ mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
     if (iter->cache == NULL)
        return KRB5_CC_END;
 
-    HEIMDAL_MUTEX_lock(&mcc_mutex);
     m = iter->cache;
-    if (m->next)
-    {
-       HEIMDAL_MUTEX_lock(&(m->next->mutex));
-       m->next->refcnt++;
-       HEIMDAL_MUTEX_unlock(&(m->next->mutex));
-    }
-
-    iter->cache = m->next;
-    HEIMDAL_MUTEX_unlock(&mcc_mutex);
+    iter->cache = mcc_get_cache_find_next_internal(m);
 
     ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
     if (ret)