@@ -5,7 +5,7 @@
.\"
.\"
-.\" Copyright 2011 Oracle. All rights reserved.
+.\" Copyright 2011, 2013 Oracle. All rights reserved.
.\"
.\" This file is part of fedfs-utils.
.\"
@@ -55,11 +55,9 @@ create and delete FedFS junctions on that file server.
Because
.BR rpc.fedfsd (8)
can operate on any object in an file server's local file systems,
-FedFS administrative clients communicate with
-.BR rpc.fedfsd (8)
-via secure RPC.
-RPCSEC GSSAPI and Kerberos must be configured and operating
-correctly to ensure proper security.
+FedFS administrative clients should use strong security
+such as Kerberos when communicating with
+.BR rpc.fedfsd (8).
.SS Command line arguments
.IP "\fB\-?, \-\-help"
Prints
@@ -134,10 +132,30 @@ provides more security than the
setting,
.B unix
is not recommended for use on untrusted networks.
+.IP "\fBgss\fP"
+This setting specifies which GSS mechanisms, services, and principals
+are authorized to perform ADMIN operations.
+Currently the only supported GSS mechanism is
+.BR kerberos_v5 .
.P
See comments in
.I /etc/fedfsd/access.conf
for details on syntax of the Access Control List.
+.P
+To enable Kerberos security via GSS, a service principal for the
+.B fedfs-admin
+service must be created for each host running
+.BR rpc.fedfsd (8).
+The resulting key must be retrieved from the KDC
+and stored in a keytab file (usually
+.IR /etc/krb5.keytab )
+on each host running
+.BR rpc.fedfsd (8).
+.P
+The exact procedure for creating a service principal and retrieving
+and storing a secret key for it depends on the type of KDC
+in use for the local Kerberos realm.
+Consult your local Kerberos realm administrator for more information.
.SH NOTES
To create, resolve, or delete a junction, FedFS admin clients
specify the pathname of that junction as an argument to the
@@ -26,14 +26,15 @@
noinst_HEADERS = fedfsd.h
RPCPREFIX = rpc.
sbin_PROGRAMS = fedfsd
-fedfsd_SOURCES = access.c listen.c main.c privilege.c svc.c
+fedfsd_SOURCES = access.c gss.c listen.c main.c privilege.c svc.c
fedfsd_LDADD = $(top_builddir)/src/libadmin/libadmin.la \
$(top_builddir)/src/libnsdb/libnsdb.la \
$(top_builddir)/src/libjunction/libjunction.la \
$(top_builddir)/src/libxlog/libxlog.la \
$(LIBTIRPC) $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
$(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
- $(LIBURIPARSER) $(LIBCRYPTO) $(LIBSSL) $(LIBCONFIG)
+ $(LIBURIPARSER) $(LIBCRYPTO) $(LIBSSL) \
+ $(LIBCONFIG) $(LIBGSSAPI_KRB5)
CLEANFILES = cscope.in.out cscope.out cscope.po.out *~
DISTCLEANFILES = Makefile.in
@@ -88,6 +88,49 @@ fedfsd_unix_is_allowed(void)
}
/**
+ * Predicate: Is RPCSEC_GSS Kerberos v5 access allowed?
+ *
+ * @return one if Kerberos access is allowed, otherwise zero
+ *
+ * There must be more than zero Kerberos principals, and at
+ * least one GSS Kerberos service enabled.
+ */
+static int
+fedfsd_gss_krb5_is_allowed(void)
+{
+ int count, value, err;
+ config_setting_t *setting;
+
+ setting = config_lookup(&fedfsd_acl,
+ "gss.kerberos_v5.allowed_principals");
+ if (setting == NULL)
+ return 0;
+ count = config_setting_length(setting);
+ if (count == 0)
+ return 0;
+
+ count = 0;
+ err = config_lookup_bool(&fedfsd_acl,
+ "gss.kerberos_v5.required_services.authentication", &value);
+ if (err == CONFIG_TRUE)
+ count += value;
+
+ err = config_lookup_bool(&fedfsd_acl,
+ "gss.kerberos_v5.required_services.integrity", &value);
+ if (err == CONFIG_TRUE)
+ count += value;
+
+ err = config_lookup_bool(&fedfsd_acl,
+ "gss.kerberos_v5.required_services.privacy", &value);
+ if (err == CONFIG_TRUE)
+ count += value;
+
+ if (count == 0)
+ return 0;
+ return 1;
+}
+
+/**
* Read and parse the access configuration file
*
* @return true if file was parsed, otherwise false
@@ -116,6 +159,7 @@ fedfsd_read_config(void)
count = fedfsd_none_is_allowed();
count += fedfsd_unix_is_allowed();
+ count += fedfsd_gss_krb5_is_allowed();
if (count == 0)
xlog(L_WARNING, "%s allows no access to the ADMIN service",
fedfsd_access_pathname);
@@ -423,3 +467,88 @@ fedfsd_auth_unix(struct svc_req *rqstp)
return fedfsd_auth_unix_group(cred->aup_gid,
cred->aup_len, cred->aup_gids);
}
+
+/**
+ * Decide if a caller string matches a list element
+ *
+ * @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
+ * @return true if "caller" matches the list element at "i"
+ */
+static _Bool
+fedfsd_check_list(config_setting_t *setting, int i, const char *caller)
+{
+ const char *name;
+
+ name = config_setting_get_string_elem(setting, i);
+ if (name == NULL)
+ return false;
+ return strcasecmp(name, caller) == 0;
+}
+
+/*
+ * Decide if an RPCSEC_GSS Kerberos v5 principal is authorized
+ *
+ * @param rqstp incoming RPC request
+ * @return true if access is authorized
+ */
+static _Bool
+fedfsd_auth_rpc_gss_krb5_principal(struct svc_req *rqstp)
+{
+ config_setting_t *principals;
+ char *principal;
+ _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)
+ goto out;
+
+ count = config_setting_length(principals);
+ for (i = 0; i < count; i++) {
+ if (fedfsd_check_list(principals, i, principal)) {
+ result = true;
+ break;
+ }
+ }
+
+out:
+ if (!result)
+ xlog(D_CALL, "%s: '%s' not authorized", __func__, principal);
+ else
+ xlog(D_CALL, "%s: '%s' authorized", __func__, principal);
+
+ free(principal);
+ return result;
+}
+
+/*
+ * Decide if an RPCSEC_GSS principal is authorized
+ *
+ * @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)
+{
+ if (!fedfsd_reread_access_config())
+ return false;
+
+ if (fedfsd_gss_krb5_is_allowed() == 0) {
+ xlog(D_CALL, "%s: GSS callers not authorized", __func__);
+ return false;
+ }
+ return fedfsd_auth_rpc_gss_krb5_principal(rqstp);
+}
@@ -46,6 +46,14 @@ _Bool fedfsd_read_access_config(const char *pathname);
_Bool fedfsd_auth_none(void);
_Bool fedfsd_auth_unix(struct svc_req *rqstp);
+_Bool fedfsd_auth_rpc_gss(struct svc_req *rqstp);
+
+/*
+ * gss.c
+ */
+extern bool_t fedfsd_no_dispatch;
+_Bool fedfsd_set_up_authenticators(void);
+char * fedfsd_get_gss_cred(struct svc_req *rqstp);
/*
* listen.c
new file mode 100644
@@ -0,0 +1,180 @@
+/**
+ * @file src/fedfsd/gss.c
+ * @brief fedfsd support for RPCSEC GSSAPI
+ *
+ * Todo: Rework when Linux libtirpc gets a standard RPCSEC API
+ */
+
+/*
+ * Copyright 2013 Oracle. All rights reserved.
+ *
+ * This file is part of fedfs-utils.
+ *
+ * fedfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * fedfs-utils is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with fedfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include <rpc/svc_auth.h>
+#include <gssapi/gssapi.h>
+
+#include "fedfs.h"
+#include "nsdb.h"
+#include "fedfsd.h"
+#include "xlog.h"
+
+
+/**
+ * Internal TI-RPC API for unpacking a GSS credential
+ * (Not currently provided by any libtirpc header)
+ */
+enum auth_stat _svcauth_gss(struct svc_req *rqst,
+ struct rpc_msg *msg,
+ bool_t *no_dispatch);
+
+/**
+ * TI-RPC API for setting the server's principal name
+ * (Not currently provided by any libtirpc header)
+ */
+bool_t svcauth_gss_set_svc_name(gss_name_t name);
+
+/**
+ * TI-RPC API for retrieving the caller's principal
+ * (Not currently provided by any libtirpc header)
+ */
+char *svcauth_gss_get_principal(SVCAUTH *auth);
+
+
+/**
+ * Set to TRUE when the GSS authenticator has already sent an RPC reply
+ */
+bool_t fedfsd_no_dispatch = FALSE;
+
+/**
+ * Log a GSS error
+ *
+ * @param prefix NUL-terminated C string containing log entry prefix
+ * @param maj_stat major status to report
+ * @param min_stat minor status to report
+ */
+static void
+fedfsd_log_gss_error(const char *prefix, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+ gss_buffer_desc maj_msg, min_msg;
+ OM_uint32 min, msg_ctx;
+
+ msg_ctx = 0;
+ gss_display_status(&min, maj_stat, GSS_C_GSS_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &maj_msg);
+ gss_display_status(&min, min_stat, GSS_C_MECH_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &min_msg);
+
+ xlog(D_GENERAL, "%s: %s - %s",
+ prefix, (char *)maj_msg.value, (char *)min_msg.value);
+
+ (void)gss_release_buffer(&min, &min_msg);
+ (void)gss_release_buffer(&min, &maj_msg);
+}
+
+/**
+ * Unmarshal GSS credentials carried by a request
+ *
+ * @param rqst handle of an incoming request
+ * @param msg RPC header information
+ * @return status returned from authentication check
+ */
+static enum auth_stat
+fedfsd_authenticate_gss(struct svc_req *rqst, struct rpc_msg *msg)
+{
+ enum auth_stat stat;
+
+ fedfsd_no_dispatch = FALSE;
+ stat = _svcauth_gss(rqst, msg, &fedfsd_no_dispatch);
+ xlog(D_GENERAL, "%s: stat = %d, no_dispatch = %d\n",
+ __func__, stat, fedfsd_no_dispatch);
+ return stat;
+}
+
+static _Bool
+fedfsd_set_svc_name(void)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc namebuf;
+ gss_name_t name;
+
+ namebuf.value = FEDFS_ADMIN_GSS_SERVICE_NAME;
+ namebuf.length = strlen(FEDFS_ADMIN_GSS_SERVICE_NAME);
+
+ maj_stat = gss_import_name(&min_stat, &namebuf,
+ (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
+ &name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ fedfsd_log_gss_error("Failed to import service name",
+ maj_stat, min_stat);
+ return false;
+ }
+
+ if (svcauth_gss_set_svc_name(name) != TRUE) {
+ (void)gss_release_name(&min_stat, &name);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Install call-outs to unmarshal each request's credentials
+ *
+ * @return true if all handlers were installed successfully.
+ *
+ * libtirpc already provides handlers for dealing with
+ * AUTH_NULL and AUTH_SYS. These cannot be removed.
+ * A handler for RPCSEC_GSS must be installed manually.
+ */
+_Bool
+fedfsd_set_up_authenticators(void)
+{
+ if (svc_auth_reg(RPCSEC_GSS, fedfsd_authenticate_gss) < 0)
+ return false;
+ return fedfsd_set_svc_name();
+}
+
+/**
+ * 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;
+
+ auth = rqstp->rq_xprt->xp_auth;
+ return svcauth_gss_get_principal(auth);
+}
@@ -230,6 +230,9 @@ int main(int argc, char **argv)
xlog(L_NOTICE, "Version " VERSION " (built %s at %s) starting",
__DATE__, __TIME__);
+ if (!fedfsd_set_up_authenticators())
+ exit(EXIT_FAILURE);
+
nsdb_connsec_crypto_startup();
/* Normally doesn't return */
@@ -74,6 +74,9 @@ fedfsd_is_authorized(struct svc_req *rqstp)
case AUTH_SYS:
authorized = fedfsd_auth_unix(rqstp);
break;
+ case RPCSEC_GSS:
+ authorized = fedfsd_auth_rpc_gss(rqstp);
+ break;
default:
xlog(L_ERROR, "Procedure %d used unsupported security flavor",
rqstp->rq_proc);
@@ -1348,6 +1351,9 @@ fedfsd_dispatch_1(struct svc_req *rqstp, SVCXPRT *xprt)
{
char addrbuf[INET6_ADDRSTRLEN];
+ if (fedfsd_no_dispatch)
+ return;
+
fedfsd_caller(rqstp, addrbuf, sizeof(addrbuf));
if (!fedfsd_is_authorized(rqstp)) {
@@ -25,3 +25,31 @@
# users = ( "fedfs", "root", 99 );
# groups = ( "wheel", 55 );
# };
+
+## Uncomment and update this setting to specify what GSS mechanisms
+## clients are allowed to use to perform ADMIN operations.
+##
+## Each element in the "gss" group describes one GSS mechanism.
+##
+## The "required_services" group specifies which GSS services are
+## allowed to perform ADMIN operations.
+##
+## The "allowed_principals" list specifies which principals are
+## allowed to perform ADMIN operations.
+# gss =
+# {
+# kerberos_v5 =
+# {
+# required_services =
+# {
+# authentication = false;
+# integrity = true;
+# privacy = true;
+# };
+# allowed_principals =
+# (
+# "alice@EXAMPLE.COM",
+# "bob@EXAMPLE.COM"
+# );
+# }
+# };
Provisional. RPCSEC support in libtirpc is incomplete, but there is enough to handle basic GSS authentication with Kerberos for one client at a time. Feature completeness is planned for a future fedfs-utils release. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- doc/man/rpc.fedfsd.8 | 30 ++++++- src/fedfsd/Makefile.am | 5 + src/fedfsd/access.c | 129 ++++++++++++++++++++++++++++++++ src/fedfsd/fedfsd.h | 8 ++ src/fedfsd/gss.c | 180 ++++++++++++++++++++++++++++++++++++++++++++ src/fedfsd/main.c | 3 + src/fedfsd/svc.c | 6 + sysconf/fedfsd/access.conf | 28 +++++++ 8 files changed, 381 insertions(+), 8 deletions(-) create mode 100644 src/fedfsd/gss.c