Blob Blame History Raw
From 37d1667ad0cc91f46a493281e62775cc8bbe3b5b Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Tue, 14 Mar 2017 10:20:08 -0400
Subject: [PATCH] Change impersonator check code

In MIT 1.15 we now have a native way to check for an impersonator,
implement the use of that function but still keep the fallback for
earlier krb5 versions that do not support this method for now.

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Robbie Harwood <rharwood@redhat.com>
Merges: #172
(cherry picked from commit 73b50c0b2799f0aed53337a6516b8e1a27279ebf)
---
 proxy/configure.ac   |   3 +
 proxy/src/gp_creds.c | 147 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 112 insertions(+), 38 deletions(-)

diff --git a/proxy/configure.ac b/proxy/configure.ac
index 63c0edf..c52dbb6 100644
--- a/proxy/configure.ac
+++ b/proxy/configure.ac
@@ -131,6 +131,9 @@ AC_CHECK_LIB(gssapi_krb5, gss_export_cred,,
              [AC_MSG_ERROR([GSSAPI library does not support gss_export_cred])],
              [$GSSAPI_LIBS])
 
+AC_CHECK_DECLS([GSS_KRB5_GET_CRED_IMPERSONATOR], [], [],
+               [[#include <gssapi/gssapi_krb5.h>]])
+
 AC_SUBST([KRB5_CFLAGS])
 AC_SUBST([KRB5_LIBS])
 AC_SUBST([GSSAPI_CFLAGS])
diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c
index 171a724..95a1c48 100644
--- a/proxy/src/gp_creds.c
+++ b/proxy/src/gp_creds.c
@@ -773,9 +773,9 @@ void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags)
     *flags &= ~gpcall->service->filter_flags;
 }
 
-uint32_t gp_cred_allowed(uint32_t *min,
-                         struct gp_call_ctx *gpcall,
-                         gss_cred_id_t cred)
+
+static uint32_t get_impersonator_fallback(uint32_t *min, gss_cred_id_t cred,
+                                          char **impersonator)
 {
     uint32_t ret_maj = 0;
     uint32_t ret_min = 0;
@@ -785,22 +785,6 @@ uint32_t gp_cred_allowed(uint32_t *min,
     krb5_data config;
     int err;
 
-    if (cred == GSS_C_NO_CREDENTIAL) {
-        return GSS_S_CRED_UNAVAIL;
-    }
-
-    if (gpcall->service->trusted ||
-        gpcall->service->impersonate ||
-        gpcall->service->allow_const_deleg) {
-
-        GPDEBUGN(2, "Credentials allowed by configuration\n");
-        *min = 0;
-        return GSS_S_COMPLETE;
-    }
-
-    /* FIXME: krb5 specific code, should get an oid registerd to query the
-     * cred with gss_inquire_cred_by_oid() or similar instead */
-
     err = krb5_init_context(&context);
     if (err) {
         ret_min = err;
@@ -835,21 +819,116 @@ uint32_t gp_cred_allowed(uint32_t *min,
         goto done;
     }
 
+    err = krb5_cc_get_config(context, ccache, NULL, "proxy_impersonator",
+                             &config);
+    if (err == 0) {
+        *impersonator = strndup(config.data, config.length);
+        if (!*impersonator) {
+            ret_min = ENOMEM;
+            ret_maj = GSS_S_FAILURE;
+        } else {
+            ret_min = 0;
+            ret_maj = GSS_S_COMPLETE;
+        }
+        krb5_free_data_contents(context, &config);
+    } else {
+        ret_min = err;
+        ret_maj = GSS_S_FAILURE;
+    }
+
+done:
+    if (context) {
+        if (ccache) {
+            krb5_cc_destroy(context, ccache);
+        }
+        krb5_free_context(context);
+    }
+    free(memcache);
+
+    *min = ret_min;
+    return ret_maj;
+}
+
+#if !HAVE_DECL_GSS_KRB5_GET_CRED_IMPERSONATOR
+gss_OID_desc impersonator_oid = {
+    11, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0e")
+};
+const gss_OID GSS_KRB5_GET_CRED_IMPERSONATOR = &impersonator_oid;
+#endif
+
+static uint32_t get_impersonator_name(uint32_t *min, gss_cred_id_t cred,
+                                      char **impersonator)
+{
+    gss_buffer_set_t bufset = GSS_C_NO_BUFFER_SET;
+    uint32_t ret_maj = 0;
+    uint32_t ret_min = 0;
+    uint32_t discard;
+
+    *impersonator = NULL;
+
+    ret_maj = gss_inquire_cred_by_oid(&ret_min, cred,
+                                      GSS_KRB5_GET_CRED_IMPERSONATOR,
+                                      &bufset);
+    if (ret_maj == GSS_S_COMPLETE) {
+        if (bufset->count == 0) {
+            ret_min = ENOENT;
+            ret_maj = GSS_S_COMPLETE;
+            goto done;
+        }
+        *impersonator = strndup(bufset->elements[0].value,
+                                bufset->elements[0].length);
+        if (!*impersonator) {
+            ret_min = ENOMEM;
+            ret_maj = GSS_S_FAILURE;
+        }
+    } else if (ret_maj == GSS_S_UNAVAILABLE) {
+        /* Not supported by krb5 library yet, fallback to raw krb5 calls */
+        /* TODO: Remove once we set a required dependency on MIT 1.15+ */
+        ret_maj = get_impersonator_fallback(&ret_min, cred, impersonator);
+        if (ret_maj == GSS_S_FAILURE) {
+            if (ret_min == KRB5_CC_NOTFOUND) {
+                ret_min = ENOENT;
+                ret_maj = GSS_S_COMPLETE;
+            }
+        }
+    }
+
+done:
+    (void)gss_release_buffer_set(&discard, &bufset);
+    *min = ret_min;
+    return ret_maj;
+}
+
+uint32_t gp_cred_allowed(uint32_t *min,
+                         struct gp_call_ctx *gpcall,
+                         gss_cred_id_t cred)
+{
+    char *impersonator = NULL;
+    uint32_t ret_maj = 0;
+    uint32_t ret_min = 0;
+
+    if (cred == GSS_C_NO_CREDENTIAL) {
+        return GSS_S_CRED_UNAVAIL;
+    }
+
+    if (gpcall->service->trusted ||
+        gpcall->service->impersonate ||
+        gpcall->service->allow_const_deleg) {
+
+        GPDEBUGN(2, "Credentials allowed by configuration\n");
+        *min = 0;
+        return GSS_S_COMPLETE;
+    }
+
+    ret_maj = get_impersonator_name(&ret_min, cred, &impersonator);
+    if (ret_maj) goto done;
+
     /* if we find an impersonator entry we bail as that is not authorized,
      * if it were then gpcall->service->allow_const_deleg would have caused
      * the ealier check to return GSS_S_COMPLETE already */
-    err = krb5_cc_get_config(context, ccache, NULL, "proxy_impersonator",
-                             &config);
-    if (!err) {
-        krb5_free_data_contents(context, &config);
+    if (impersonator != NULL) {
         ret_min = 0;
         ret_maj = GSS_S_UNAUTHORIZED;
-    } else if (err != KRB5_CC_NOTFOUND) {
-        ret_min = err;
-        ret_maj = GSS_S_FAILURE;
-    } else {
-        ret_min = 0;
-        ret_maj = GSS_S_COMPLETE;
     }
 
 done:
@@ -864,15 +943,7 @@ done:
         GPDEBUG("Failure while checking credentials\n");
         break;
     }
-    if (context) {
-        /* NOTE: destroy only if we created a MEMORY ccache */
-        if (ccache) {
-            if (memcache) krb5_cc_destroy(context, ccache);
-            else krb5_cc_close(context, ccache);
-        }
-        krb5_free_context(context);
-    }
-    free(memcache);
+    free(impersonator);
     *min = ret_min;
     return ret_maj;
 }