diff mbox

[4/5] fedfsd: Add RPCSEC_GSS support to fedfsd

Message ID 20131218171802.7774.30067.stgit@seurat.1015granger.net
State Accepted
Headers show

Commit Message

Chuck Lever Dec. 18, 2013, 5:18 p.m. UTC
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
diff mbox

Patch

diff --git a/doc/man/rpc.fedfsd.8 b/doc/man/rpc.fedfsd.8
index 668cfc1..6241d2b 100644
--- a/doc/man/rpc.fedfsd.8
+++ b/doc/man/rpc.fedfsd.8
@@ -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
diff --git a/src/fedfsd/Makefile.am b/src/fedfsd/Makefile.am
index a557bbd..269328b 100644
--- a/src/fedfsd/Makefile.am
+++ b/src/fedfsd/Makefile.am
@@ -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
diff --git a/src/fedfsd/access.c b/src/fedfsd/access.c
index dd77bd1..ffee857 100644
--- a/src/fedfsd/access.c
+++ b/src/fedfsd/access.c
@@ -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);
+}
diff --git a/src/fedfsd/fedfsd.h b/src/fedfsd/fedfsd.h
index c199818..240524a 100644
--- a/src/fedfsd/fedfsd.h
+++ b/src/fedfsd/fedfsd.h
@@ -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
diff --git a/src/fedfsd/gss.c b/src/fedfsd/gss.c
new file mode 100644
index 0000000..c63f42f
--- /dev/null
+++ b/src/fedfsd/gss.c
@@ -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);
+}
diff --git a/src/fedfsd/main.c b/src/fedfsd/main.c
index 54fd91e..028435f 100644
--- a/src/fedfsd/main.c
+++ b/src/fedfsd/main.c
@@ -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 */
diff --git a/src/fedfsd/svc.c b/src/fedfsd/svc.c
index 538def4..391583b 100644
--- a/src/fedfsd/svc.c
+++ b/src/fedfsd/svc.c
@@ -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)) {
diff --git a/sysconf/fedfsd/access.conf b/sysconf/fedfsd/access.conf
index 7871f6f..7d421b3 100644
--- a/sysconf/fedfsd/access.conf
+++ b/sysconf/fedfsd/access.conf
@@ -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"
+#     );
+#   }
+# };