Use a GSS callback to verify a client is using a permitted service
and mechanism. fedfsd can now reject requests that do not have an
appropriately high service level (say, at least "integrity") before
even looking at the incoming principal.

Signed-off-by: Chuck Lever <[email protected]>
---
 src/fedfsd/access.c |  125 ++++++++++++++++++++++++++++++++++-----------------
 src/fedfsd/fedfsd.h |    3 -
 src/fedfsd/gss.c    |   58 ++++++++++++++----------
 src/fedfsd/svc.c    |    2 -
 4 files changed, 120 insertions(+), 68 deletions(-)

diff --git a/src/fedfsd/access.c b/src/fedfsd/access.c
index ffee857..92f7976 100644
--- a/src/fedfsd/access.c
+++ b/src/fedfsd/access.c
@@ -36,6 +36,7 @@
 #include <linux/limits.h>
 #include <rpc/auth.h>
 #include <rpc/auth_unix.h>
+#include <rpc/rpcsec_gss.h>
 #include <libconfig.h>
 
 #include "fedfs.h"
@@ -473,57 +474,75 @@ fedfsd_auth_unix(struct svc_req *rqstp)
  *
  * @param setting config setting containing a list
  * @param i index of list element to check
- * @param caller NUL-terminated C string containing principal to check
+ * @param caller C string containing principal to check
+ * @param len length of "caller" in bytes
  * @return true if "caller" matches the list element at "i"
  */
 static _Bool
-fedfsd_check_list(config_setting_t *setting, int i, const char *caller)
+fedfsd_check_list(config_setting_t *setting, int i, const char *caller, int 
len)
 {
        const char *name;
 
        name = config_setting_get_string_elem(setting, i);
        if (name == NULL)
                return false;
-       return strcasecmp(name, caller) == 0;
+       return strncasecmp(name, caller, len) == 0;
 }
 
-/*
- * Decide if an RPCSEC_GSS Kerberos v5 principal is authorized
+/**
+ * Decide if an RPCSEC_GSS context may be established
  *
- * @param rqstp incoming RPC request
- * @return true if access is authorized
+ * @param rcred raw RPC GSS credential
+ * @result true if credential is authorized
+ *
+ * The only supported mechanism today is Kerberos 5.
  */
-static _Bool
-fedfsd_auth_rpc_gss_krb5_principal(struct svc_req *rqstp)
+_Bool
+fedfsd_auth_rpc_gss_allowed(rpc_gss_rawcred_t *rcred)
 {
-       config_setting_t *principals;
-       char *principal;
+       int value, err;
+       const char *path;
        _Bool result;
-       int i, count;
-
-       principal = fedfsd_get_gss_cred(rqstp);
 
        result = false;
-       principals = config_lookup(&fedfsd_acl,
-                                       "gss.kerberos_v5.allowed_principals");
-       if (principals == NULL)
+       if (!fedfsd_reread_access_config())
+               goto out;
+       if (fedfsd_gss_krb5_is_allowed() == 0) {
+               xlog(D_CALL, "%s: GSS callers not authorized", __func__);
                goto out;
+       }
 
-       count = config_setting_length(principals);
-       for (i = 0; i < count; i++) {
-               if (fedfsd_check_list(principals, i, principal)) {
-                       result = true;
-                       break;
-               }
+       if (strcmp(rcred->mechanism, "kerberos_v5") != 0) {
+               xlog(D_CALL, "%s: mechanism not supported", __func__);
+               goto out;
        }
 
-out:
-       if (!result)
-               xlog(D_CALL, "%s: '%s' not authorized", __func__, principal);
-       else
-               xlog(D_CALL, "%s: '%s' authorized", __func__, principal);
+       if (strcmp(rcred->qop, "GSS_C_QOP_DEFAULT") != 0) {
+               xlog(D_CALL, "%s: mechanism not supported", __func__);
+               goto out;
+       }
+
+       switch (rcred->service) {
+       case rpcsec_gss_svc_integrity:
+               path = "gss.kerberos_v5.required_services.integrity";
+               break;
+       case rpcsec_gss_svc_privacy:
+               path = "gss.kerberos_v5.required_services.privacy";
+               break;
+       default:
+               path = "gss.kerberos_v5.required_services.authentication";
+       }
+       err = config_lookup_bool(&fedfsd_acl, path, &value);
+       if (err == CONFIG_FALSE)
+               goto out;       /* "path not there" is the same as "false" */
+       if (value == 0)
+               goto out;
+
+       result = true;
 
-       free(principal);
+out:
+       xlog(D_CALL, "%s: mechanism and service %s authorized",
+               __func__, result ? "are" : "are not");
        return result;
 }
 
@@ -532,23 +551,47 @@ out:
  *
  * @param rqstp incoming RPC request
  * @return true if access is authorized
- *
- * This is provisional because the current libtirpc GSS API provides
- * only the caller's princpal, not the GSS mechanism or the GSS
- * service.
- *
- * For now, assume that the GSS mechanism is always "Kerberos v5" and
- * don't check to see if the service is enabled.
  */
 _Bool
 fedfsd_auth_rpc_gss(struct svc_req *rqstp)
 {
+       config_setting_t *principals;
+       rpc_gss_rawcred_t *rcred;
+       rpc_gss_principal_t cp;
+       int i, count;
+       _Bool result;
+       void *cookie;
+
+       result = false;
        if (!fedfsd_reread_access_config())
-               return false;
+               goto out;
+       if (fedfsd_gss_krb5_is_allowed() == 0)
+               goto out;
 
-       if (fedfsd_gss_krb5_is_allowed() == 0) {
-               xlog(D_CALL, "%s: GSS callers not authorized", __func__);
-               return false;
+       /* Check if the GSS context is trusted */
+       if (rpc_gss_getcred(rqstp, &rcred, NULL, &cookie) == FALSE)
+               goto out;
+       if (cookie == NULL)
+               goto out;
+
+       /* Check if the principal is allowed */
+       cp = rcred->client_principal;
+       principals = config_lookup(&fedfsd_acl,
+                                       "gss.kerberos_v5.allowed_principals");
+       if (principals == NULL)
+               goto out;
+
+       count = config_setting_length(principals);
+       for (i = 0; i < count; i++) {
+               if (fedfsd_check_list(principals, i, cp->name, cp->len)) {
+                       result = true;
+                       break;
+               }
        }
-       return fedfsd_auth_rpc_gss_krb5_principal(rqstp);
+
+       xlog(D_CALL, "%s: Principal '%*s' %s authorized", __func__,
+               cp->len, cp->name, (result ? "is" : "is not"));
+
+out:
+       return result;
 }
diff --git a/src/fedfsd/fedfsd.h b/src/fedfsd/fedfsd.h
index 220e7e1..3c128b9 100644
--- a/src/fedfsd/fedfsd.h
+++ b/src/fedfsd/fedfsd.h
@@ -40,7 +40,7 @@
 #define FEDFSD_ACCESS_CONFIG   "/etc/fedfsd/access.conf"
 
 /*
- * auth.c
+ * access.c
  */
 _Bool          fedfsd_read_access_config(const char *pathname);
 
@@ -52,7 +52,6 @@ _Bool         fedfsd_auth_rpc_gss(struct svc_req *rqstp);
  * gss.c
  */
 _Bool          fedfsd_set_up_authenticators(void);
-char *         fedfsd_get_gss_cred(struct svc_req *rqstp);
 
 /*
  * listen.c
diff --git a/src/fedfsd/gss.c b/src/fedfsd/gss.c
index 639f204..fe92986 100644
--- a/src/fedfsd/gss.c
+++ b/src/fedfsd/gss.c
@@ -36,22 +36,46 @@
 #include <netinet/in.h>
 
 #include <rpc/rpc.h>
-#include <rpc/auth.h>
-#include <rpc/svc.h>
 #include <rpc/svc_auth.h>
 #include <rpc/rpcsec_gss.h>
 
 #include "fedfs.h"
-#include "nsdb.h"
 #include "fedfsd.h"
 #include "xlog.h"
 
+_Bool          fedfsd_auth_rpc_gss_allowed(rpc_gss_rawcred_t *);
 
 /**
- * TI-RPC API for retrieving the caller's principal
- * (Not currently provided by any libtirpc header)
+ * Verify that incoming GSS context may be established
+ *
+ * @param rqstp incoming RPC request
+ * @param cred delegated GSS credentials
+ * @param ctxt GSS context
+ * @param lock enforce a particular QOP and service
+ * @param cookie not used
+ * @result true if GSS context may be established
  */
-char                   *svcauth_gss_get_principal(SVCAUTH *auth);
+static bool_t
+fedfsd_gss_new_context(__attribute__((unused)) struct svc_req *rqstp,
+                      __attribute__((unused)) gss_cred_id_t cred,
+                      __attribute__((unused)) gss_ctx_id_t ctxt,
+                      rpc_gss_lock_t *lock,
+                      void **cookie)
+{
+       *cookie = NULL;
+       if (!fedfsd_auth_rpc_gss_allowed(lock->raw_cred))
+               return FALSE;
+
+       *cookie = (void *)1;
+       lock->locked = TRUE;
+       return TRUE;
+}
+
+static rpc_gss_callback_t fedfsd_gss_callback = {
+       .program        = FEDFS_PROG,
+       .version        = FEDFS_V1,
+       .callback       = fedfsd_gss_new_context,
+};
 
 /**
  * Ensure GSS Kerberos authentication is enabled
@@ -74,22 +98,10 @@ fedfsd_set_up_authenticators(void)
                return false;
        }
 
-       return true;
-}
-
-/**
- * Extract the RPCSEC GSS principal from an incoming request
- *
- * @param rqstp incoming RPC request
- * @return NUL-terminated C string containing GSS principal
- *
- * Caller must free principal with free(3).
- */
-char *
-fedfsd_get_gss_cred(struct svc_req *rqstp)
-{
-       SVCAUTH *auth;
+       if (!rpc_gss_set_callback(&fedfsd_gss_callback)) {
+               xlog(D_GENERAL, "%s: Could not set GSS callback", __func__);
+               return false;
+       }
 
-       auth = rqstp->rq_xprt->xp_auth;
-       return svcauth_gss_get_principal(auth);
+       return true;
 }
diff --git a/src/fedfsd/svc.c b/src/fedfsd/svc.c
index ea057d4..2e97924 100644
--- a/src/fedfsd/svc.c
+++ b/src/fedfsd/svc.c
@@ -2,8 +2,6 @@
  * @file src/fedfsd/svc.c
  * @brief Convert incoming FedFS admin RPC requests into local function calls.
  *
- * @todo
- *     Support RPCGSS authentication of clients
  */
 
 /*


_______________________________________________
fedfs-utils-devel mailing list
[email protected]
https://oss.oracle.com/mailman/listinfo/fedfs-utils-devel

Reply via email to