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 <[email protected]> --- 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 --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 = +# ( +# "[email protected]", +# "[email protected]" +# ); +# } +# }; _______________________________________________ fedfs-utils-devel mailing list [email protected] https://oss.oracle.com/mailman/listinfo/fedfs-utils-devel
