Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 9 Jul 2013 01:05:29 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r253049 - in head/sys: fs/nfs fs/nfsclient kgssapi rpc rpc/rpcsec_gss sys
Message-ID:  <201307090105.r6915Tmi082145@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rmacklem
Date: Tue Jul  9 01:05:28 2013
New Revision: 253049
URL: http://svnweb.freebsd.org/changeset/base/253049

Log:
  Add support for host-based (Kerberos 5 service principal) initiator
  credentials to the kernel rpc. Modify the NFSv4 client to add
  support for the gssname and allgssname mount options to use this
  capability. Requires the gssd daemon to be running with the "-h" option.
  
  Reviewed by:	jhb

Modified:
  head/sys/fs/nfs/nfs.h
  head/sys/fs/nfs/nfs_commonkrpc.c
  head/sys/fs/nfsclient/nfs_clvfsops.c
  head/sys/kgssapi/gss_impl.c
  head/sys/rpc/rpcsec_gss.h
  head/sys/rpc/rpcsec_gss/rpcsec_gss.c
  head/sys/sys/param.h

Modified: head/sys/fs/nfs/nfs.h
==============================================================================
--- head/sys/fs/nfs/nfs.h	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/fs/nfs/nfs.h	Tue Jul  9 01:05:28 2013	(r253049)
@@ -466,6 +466,7 @@ struct nfssockreq {
 	u_int32_t	nr_prog;
 	u_int32_t	nr_vers;
 	struct __rpc_client *nr_client;
+	AUTH		*nr_auth;
 };
 
 /*

Modified: head/sys/fs/nfs/nfs_commonkrpc.c
==============================================================================
--- head/sys/fs/nfs/nfs_commonkrpc.c	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/fs/nfs/nfs_commonkrpc.c	Tue Jul  9 01:05:28 2013	(r253049)
@@ -102,7 +102,6 @@ static int	nfs_bufpackets = 4;
 static int	nfs_reconnects;
 static int	nfs3_jukebox_delay = 10;
 static int	nfs_skip_wcc_data_onerr = 1;
-static int	nfs_keytab_enctype = ETYPE_DES_CBC_CRC;
 
 SYSCTL_DECL(_vfs_nfs);
 
@@ -114,8 +113,6 @@ SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs3_juke
     "Number of seconds to delay a retry after receiving EJUKEBOX");
 SYSCTL_INT(_vfs_nfs, OID_AUTO, skip_wcc_data_onerr, CTLFLAG_RW, &nfs_skip_wcc_data_onerr, 0,
     "Disable weak cache consistency checking when server returns an error");
-SYSCTL_INT(_vfs_nfs, OID_AUTO, keytab_enctype, CTLFLAG_RW, &nfs_keytab_enctype, 0,
-    "Encryption type for the keytab entry used by nfs");
 
 static void	nfs_down(struct nfsmount *, struct thread *, const char *,
     int, int);
@@ -393,9 +390,6 @@ nfs_getauth(struct nfssockreq *nrp, int 
 {
 	rpc_gss_service_t svc;
 	AUTH *auth;
-#ifdef notyet
-	rpc_gss_options_req_t req_options;
-#endif
 
 	switch (secflavour) {
 	case RPCSEC_GSS_KRB5:
@@ -411,28 +405,16 @@ nfs_getauth(struct nfssockreq *nrp, int 
 			svc = rpc_gss_svc_integrity;
 		else
 			svc = rpc_gss_svc_privacy;
-#ifdef notyet
-		req_options.req_flags = GSS_C_MUTUAL_FLAG;
-		req_options.time_req = 0;
-		req_options.my_cred = GSS_C_NO_CREDENTIAL;
-		req_options.input_channel_bindings = NULL;
-		req_options.enc_type = nfs_keytab_enctype;
-
-		auth = rpc_gss_secfind_call(nrp->nr_client, cred,
-		    clnt_principal, srv_principal, mech_oid, svc,
-		    &req_options);
-#else
-		/*
-		 * Until changes to the rpcsec_gss code are committed,
-		 * there is no support for host based initiator
-		 * principals. As such, that case cannot yet be handled.
-		 */
+
 		if (clnt_principal == NULL)
 			auth = rpc_gss_secfind_call(nrp->nr_client, cred,
 			    srv_principal, mech_oid, svc);
-		else
-			auth = NULL;
-#endif
+		else {
+			auth = rpc_gss_seccreate_call(nrp->nr_client, cred,
+			    clnt_principal, srv_principal, "kerberosv5",
+			    svc, NULL, NULL, NULL);
+			return (auth);
+		}
 		if (auth != NULL)
 			return (auth);
 		/* fallthrough */
@@ -505,7 +487,7 @@ newnfs_request(struct nfsrv_descript *nd
 	struct rpc_callextra ext;
 	enum clnt_stat stat;
 	struct nfsreq *rep = NULL;
-	char *srv_principal = NULL;
+	char *srv_principal = NULL, *clnt_principal = NULL;
 	sigset_t oldset;
 	struct ucred *authcred;
 
@@ -568,6 +550,7 @@ newnfs_request(struct nfsrv_descript *nd
 			 */
 			if (nmp->nm_krbnamelen > 0) {
 				usegssname = 1;
+				clnt_principal = nmp->nm_krbname;
 			} else if (nmp->nm_uid != (uid_t)-1) {
 				KASSERT(nmp->nm_sockreq.nr_cred != NULL,
 				    ("newnfs_request: NULL nr_cred"));
@@ -622,10 +605,19 @@ newnfs_request(struct nfsrv_descript *nd
 
 	if (nd->nd_procnum == NFSPROC_NULL)
 		auth = authnone_create();
-	else if (usegssname)
-		auth = nfs_getauth(nrp, secflavour, nmp->nm_krbname,
-		    srv_principal, NULL, authcred);
-	else
+	else if (usegssname) {
+		/*
+		 * For this case, the authenticator is held in the
+		 * nfssockreq structure, so don't release the reference count
+		 * held on it. --> Don't AUTH_DESTROY() it in this function.
+		 */
+		if (nrp->nr_auth == NULL)
+			nrp->nr_auth = nfs_getauth(nrp, secflavour,
+			    clnt_principal, srv_principal, NULL, authcred);
+		else
+			rpc_gss_refresh_auth_call(nrp->nr_auth);
+		auth = nrp->nr_auth;
+	} else
 		auth = nfs_getauth(nrp, secflavour, NULL,
 		    srv_principal, NULL, authcred);
 	crfree(authcred);
@@ -781,7 +773,8 @@ tryagain:
 	}
 	if (error) {
 		m_freem(nd->nd_mreq);
-		AUTH_DESTROY(auth);
+		if (usegssname == 0)
+			AUTH_DESTROY(auth);
 		if (rep != NULL)
 			FREE((caddr_t)rep, M_NFSDREQ);
 		if (set_sigset)
@@ -991,7 +984,8 @@ tryagain:
 #endif
 
 	m_freem(nd->nd_mreq);
-	AUTH_DESTROY(auth);
+	if (usegssname == 0)
+		AUTH_DESTROY(auth);
 	if (rep != NULL)
 		FREE((caddr_t)rep, M_NFSDREQ);
 	if (set_sigset)
@@ -1000,7 +994,8 @@ tryagain:
 nfsmout:
 	mbuf_freem(nd->nd_mrep);
 	mbuf_freem(nd->nd_mreq);
-	AUTH_DESTROY(auth);
+	if (usegssname == 0)
+		AUTH_DESTROY(auth);
 	if (rep != NULL)
 		FREE((caddr_t)rep, M_NFSDREQ);
 	if (set_sigset)

Modified: head/sys/fs/nfsclient/nfs_clvfsops.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clvfsops.c	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/fs/nfsclient/nfs_clvfsops.c	Tue Jul  9 01:05:28 2013	(r253049)
@@ -1446,6 +1446,8 @@ bad:
 		nfscl_clientrelease(clp);
 	newnfs_disconnect(&nmp->nm_sockreq);
 	crfree(nmp->nm_sockreq.nr_cred);
+	if (nmp->nm_sockreq.nr_auth != NULL)
+		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
 	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
 	mtx_destroy(&nmp->nm_mtx);
 	if (nmp->nm_clp != NULL) {
@@ -1516,7 +1518,8 @@ nfs_unmount(struct mount *mp, int mntfla
 	newnfs_disconnect(&nmp->nm_sockreq);
 	crfree(nmp->nm_sockreq.nr_cred);
 	FREE(nmp->nm_nam, M_SONAME);
-
+	if (nmp->nm_sockreq.nr_auth != NULL)
+		AUTH_DESTROY(nmp->nm_sockreq.nr_auth);
 	mtx_destroy(&nmp->nm_sockreq.nr_mtx);
 	mtx_destroy(&nmp->nm_mtx);
 	TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)

Modified: head/sys/kgssapi/gss_impl.c
==============================================================================
--- head/sys/kgssapi/gss_impl.c	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/kgssapi/gss_impl.c	Tue Jul  9 01:05:28 2013	(r253049)
@@ -286,6 +286,7 @@ kgssapi_modevent(module_t mod, int type,
 
 	switch (type) {
 	case MOD_LOAD:
+		rpc_gss_entries.rpc_gss_refresh_auth = rpc_gss_refresh_auth;
 		rpc_gss_entries.rpc_gss_secfind = rpc_gss_secfind;
 		rpc_gss_entries.rpc_gss_secpurge = rpc_gss_secpurge;
 		rpc_gss_entries.rpc_gss_seccreate = rpc_gss_seccreate;

Modified: head/sys/rpc/rpcsec_gss.h
==============================================================================
--- head/sys/rpc/rpcsec_gss.h	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/rpc/rpcsec_gss.h	Tue Jul  9 01:05:28 2013	(r253049)
@@ -153,9 +153,9 @@ typedef AUTH	*rpc_gss_secfind_ftype(CLIE
 		    rpc_gss_service_t service);
 typedef void	rpc_gss_secpurge_ftype(CLIENT *clnt);
 typedef AUTH	*rpc_gss_seccreate_ftype(CLIENT *clnt, struct ucred *cred,
-		    const char *principal, const char *mechanism,
-		    rpc_gss_service_t service, const char *qop,
-		    rpc_gss_options_req_t *options_req,
+		    const char *clnt_principal, const char *principal,
+		    const char *mechanism, rpc_gss_service_t service,
+		    const char *qop, rpc_gss_options_req_t *options_req,
 		    rpc_gss_options_ret_t *options_ret);
 typedef bool_t	rpc_gss_set_defaults_ftype(AUTH *auth,
 		    rpc_gss_service_t service, const char *qop);
@@ -183,6 +183,7 @@ typedef bool_t	rpc_gss_get_principal_nam
 		    const char *domain);
 typedef int	rpc_gss_svc_max_data_length_ftype(struct svc_req *req,
 		    int max_tp_unit_len);
+typedef void	rpc_gss_refresh_auth_ftype(AUTH *auth);
 
 struct rpc_gss_entries {
 	rpc_gss_secfind_ftype		*rpc_gss_secfind;
@@ -204,6 +205,7 @@ struct rpc_gss_entries {
 	rpc_gss_clear_callback_ftype	*rpc_gss_clear_callback;
 	rpc_gss_get_principal_name_ftype *rpc_gss_get_principal_name;
 	rpc_gss_svc_max_data_length_ftype *rpc_gss_svc_max_data_length;
+	rpc_gss_refresh_auth_ftype	*rpc_gss_refresh_auth;
 };
 extern struct rpc_gss_entries	rpc_gss_entries;
 
@@ -229,16 +231,17 @@ rpc_gss_secpurge_call(CLIENT *clnt)
 }
 
 static __inline AUTH *
-rpc_gss_seccreate_call(CLIENT *clnt, struct ucred *cred, const char *principal,
-    const char *mechanism, rpc_gss_service_t service, const char *qop,
+rpc_gss_seccreate_call(CLIENT *clnt, struct ucred *cred,
+    const char *clnt_principal, const char *principal, const char *mechanism,
+    rpc_gss_service_t service, const char *qop,
     rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
 {
 	AUTH *ret = NULL;
 
 	if (rpc_gss_entries.rpc_gss_seccreate != NULL)
 		ret = (*rpc_gss_entries.rpc_gss_seccreate)(clnt, cred,
-		    principal, mechanism, service, qop, options_req,
-		    options_ret);
+		    clnt_principal, principal, mechanism, service, qop,
+		    options_req, options_ret);
 	return (ret);
 }
 
@@ -406,14 +409,29 @@ rpc_gss_svc_max_data_length_call(struct 
 	return (ret);
 }
 
+static __inline void
+rpc_gss_refresh_auth_call(AUTH *auth)
+{
+
+	if (rpc_gss_entries.rpc_gss_refresh_auth != NULL)
+		(*rpc_gss_entries.rpc_gss_refresh_auth)(auth);
+}
+
 AUTH	*rpc_gss_secfind(CLIENT *clnt, struct ucred *cred,
     const char *principal, gss_OID mech_oid, rpc_gss_service_t service);
 void	rpc_gss_secpurge(CLIENT *clnt);
-#endif
+void	rpc_gss_refresh_auth(AUTH *auth);
+AUTH	*rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred,
+    const char *clnt_principal, const char *principal,
+    const char *mechanism, rpc_gss_service_t service,
+    const char *qop, rpc_gss_options_req_t *options_req,
+    rpc_gss_options_ret_t *options_ret);
+#else	/* !_KERNEL */
 AUTH	*rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred,
     const char *principal, const char *mechanism, rpc_gss_service_t service,
     const char *qop, rpc_gss_options_req_t *options_req,
     rpc_gss_options_ret_t *options_ret);
+#endif	/* _KERNEL */
 bool_t	rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service,
     const char *qop);
 int	rpc_gss_max_data_length(AUTH *handle, int max_tp_unit_len);

Modified: head/sys/rpc/rpcsec_gss/rpcsec_gss.c
==============================================================================
--- head/sys/rpc/rpcsec_gss/rpcsec_gss.c	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/rpc/rpcsec_gss/rpcsec_gss.c	Tue Jul  9 01:05:28 2013	(r253049)
@@ -82,6 +82,8 @@ __FBSDID("$FreeBSD$");
 #include <rpc/rpc.h>
 #include <rpc/rpcsec_gss.h>
 
+#include <kgssapi/krb5/kcrypto.h>
+
 #include "rpcsec_gss_int.h"
 
 static void	rpc_gss_nextverf(AUTH*);
@@ -122,6 +124,7 @@ struct rpc_gss_data {
 	AUTH			*gd_auth;	/* link back to AUTH */
 	struct ucred		*gd_ucred;	/* matching local cred */
 	char			*gd_principal;	/* server principal name */
+	char			*gd_clntprincipal; /* client principal name */
 	rpc_gss_options_req_t	gd_options;	/* GSS context options */
 	enum rpcsec_gss_state	gd_state;	/* connection state */
 	gss_buffer_desc		gd_verf;	/* save GSS_S_COMPLETE
@@ -153,7 +156,7 @@ static struct sx rpc_gss_lock;
 static int rpc_gss_count;
 
 static AUTH *rpc_gss_seccreate_int(CLIENT *, struct ucred *, const char *,
-    gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
+    const char *, gss_OID, rpc_gss_service_t, u_int, rpc_gss_options_req_t *,
     rpc_gss_options_ret_t *);
 
 static void
@@ -251,8 +254,8 @@ again:
 	/*
 	 * We missed in the cache - create a new association.
 	 */
-	auth = rpc_gss_seccreate_int(clnt, cred, principal, mech_oid, service,
-	    GSS_C_QOP_DEFAULT, NULL, NULL);
+	auth = rpc_gss_seccreate_int(clnt, cred, NULL, principal, mech_oid,
+	    service, GSS_C_QOP_DEFAULT, NULL, NULL);
 	if (!auth)
 		return (NULL);
 
@@ -304,9 +307,10 @@ rpc_gss_secpurge(CLIENT *clnt)
 }
 
 AUTH *
-rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *principal,
-    const char *mechanism, rpc_gss_service_t service, const char *qop,
-    rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
+rpc_gss_seccreate(CLIENT *clnt, struct ucred *cred, const char *clnt_principal,
+    const char *principal, const char *mechanism, rpc_gss_service_t service,
+    const char *qop, rpc_gss_options_req_t *options_req,
+    rpc_gss_options_ret_t *options_ret)
 {
 	gss_OID			oid;
 	u_int			qop_num;
@@ -324,13 +328,33 @@ rpc_gss_seccreate(CLIENT *clnt, struct u
 		qop_num = GSS_C_QOP_DEFAULT;
 	}
 
-	return (rpc_gss_seccreate_int(clnt, cred, principal, oid, service,
-		qop_num, options_req, options_ret));
+	return (rpc_gss_seccreate_int(clnt, cred, clnt_principal, principal,
+		oid, service, qop_num, options_req, options_ret));
+}
+
+void
+rpc_gss_refresh_auth(AUTH *auth)
+{
+	struct rpc_gss_data	*gd;
+	rpc_gss_options_ret_t	options;
+
+	gd = AUTH_PRIVATE(auth);
+	/*
+	 * If the state != ESTABLISHED, try and initialize
+	 * the authenticator again. This will happen if the
+	 * user's credentials have expired. It may succeed now,
+	 * if they have done a kinit or similar.
+	 */
+	if (gd->gd_state != RPCSEC_GSS_ESTABLISHED) {
+		memset(&options, 0, sizeof (options));
+		(void) rpc_gss_init(auth, &options);
+	}
 }
 
 static AUTH *
-rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred, const char *principal,
-    gss_OID mech_oid, rpc_gss_service_t service, u_int qop_num,
+rpc_gss_seccreate_int(CLIENT *clnt, struct ucred *cred,
+    const char *clnt_principal, const char *principal, gss_OID mech_oid,
+    rpc_gss_service_t service, u_int qop_num,
     rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
 {
 	AUTH			*auth;
@@ -379,6 +403,10 @@ rpc_gss_seccreate_int(CLIENT *clnt, stru
 	gd->gd_auth = auth;
 	gd->gd_ucred = crdup(cred);
 	gd->gd_principal = strdup(principal, M_RPC);
+	if (clnt_principal != NULL)
+		gd->gd_clntprincipal = strdup(clnt_principal, M_RPC);
+	else
+		gd->gd_clntprincipal = NULL;
 
 
 	if (options_req) {
@@ -719,6 +747,8 @@ rpc_gss_init(AUTH *auth, rpc_gss_options
 	OM_uint32		 maj_stat, min_stat, call_stat;
 	const char		*mech;
 	struct rpc_callextra	 ext;
+	gss_OID			mech_oid;
+	gss_OID_set		mechlist;
 
 	rpc_gss_log_debug("in rpc_gss_refresh()");
 	
@@ -745,6 +775,65 @@ rpc_gss_init(AUTH *auth, rpc_gss_options
 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
 	gd->gd_cred.gc_seq = 0;
 
+	/*
+	 * For KerberosV, if there is a client principal name, that implies
+	 * that this is a host based initiator credential in the default
+	 * keytab file. For this case, it is necessary to do a
+	 * gss_acquire_cred(). When this is done, the gssd daemon will
+	 * do the equivalent of "kinit -k" to put a TGT for the name in
+	 * the credential cache file for the gssd daemon.
+	 */
+	if (gd->gd_clntprincipal != NULL &&
+	    rpc_gss_mech_to_oid("kerberosv5", &mech_oid) &&
+	    gd->gd_mech == mech_oid) {
+		/* Get rid of any old credential. */
+		if (gd->gd_options.my_cred != GSS_C_NO_CREDENTIAL) {
+			gss_release_cred(&min_stat, &gd->gd_options.my_cred);
+			gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
+		}
+	
+		/*
+		 * The mechanism must be set to KerberosV for acquisition
+		 * of credentials to work reliably.
+		 */
+		maj_stat = gss_create_empty_oid_set(&min_stat, &mechlist);
+		if (maj_stat != GSS_S_COMPLETE) {
+			options_ret->major_status = maj_stat;
+			options_ret->minor_status = min_stat;
+			goto out;
+		}
+		maj_stat = gss_add_oid_set_member(&min_stat, gd->gd_mech,
+		    &mechlist);
+		if (maj_stat != GSS_S_COMPLETE) {
+			options_ret->major_status = maj_stat;
+			options_ret->minor_status = min_stat;
+			gss_release_oid_set(&min_stat, &mechlist);
+			goto out;
+		}
+	
+		principal_desc.value = (void *)gd->gd_clntprincipal;
+		principal_desc.length = strlen(gd->gd_clntprincipal);
+		maj_stat = gss_import_name(&min_stat, &principal_desc,
+		    GSS_C_NT_HOSTBASED_SERVICE, &name);
+		if (maj_stat != GSS_S_COMPLETE) {
+			options_ret->major_status = maj_stat;
+			options_ret->minor_status = min_stat;
+			gss_release_oid_set(&min_stat, &mechlist);
+			goto out;
+		}
+		/* Acquire the credentials. */
+		maj_stat = gss_acquire_cred(&min_stat, name, 0,
+		    mechlist, GSS_C_INITIATE,
+		    &gd->gd_options.my_cred, NULL, NULL);
+		gss_release_name(&min_stat, &name);
+		gss_release_oid_set(&min_stat, &mechlist);
+		if (maj_stat != GSS_S_COMPLETE) {
+			options_ret->major_status = maj_stat;
+			options_ret->minor_status = min_stat;
+			goto out;
+		}
+	}
+
 	principal_desc.value = (void *)gd->gd_principal;
 	principal_desc.length = strlen(gd->gd_principal);
 	maj_stat = gss_import_name(&min_stat, &principal_desc,
@@ -1036,6 +1125,8 @@ rpc_gss_destroy(AUTH *auth)
 	CLNT_RELEASE(gd->gd_clnt);
 	crfree(gd->gd_ucred);
 	free(gd->gd_principal, M_RPC);
+	if (gd->gd_clntprincipal != NULL)
+		free(gd->gd_clntprincipal, M_RPC);
 	if (gd->gd_verf.value)
 		xdr_free((xdrproc_t) xdr_gss_buffer_desc,
 		    (char *) &gd->gd_verf);

Modified: head/sys/sys/param.h
==============================================================================
--- head/sys/sys/param.h	Mon Jul  8 21:25:12 2013	(r253048)
+++ head/sys/sys/param.h	Tue Jul  9 01:05:28 2013	(r253049)
@@ -58,7 +58,7 @@
  *		in the range 5 to 9.
  */
 #undef __FreeBSD_version
-#define __FreeBSD_version 1000035	/* Master, propagated to newvers */
+#define __FreeBSD_version 1000036	/* Master, propagated to newvers */
 
 /*
  * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201307090105.r6915Tmi082145>