summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/fs/nfs/nfs.h1
-rw-r--r--sys/fs/nfs/nfs_commonkrpc.c61
-rw-r--r--sys/fs/nfsclient/nfs_clvfsops.c5
-rw-r--r--sys/kgssapi/gss_impl.c1
-rw-r--r--sys/rpc/rpcsec_gss.h34
-rw-r--r--sys/rpc/rpcsec_gss/rpcsec_gss.c111
-rw-r--r--sys/sys/param.h2
7 files changed, 162 insertions, 53 deletions
diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h
index febb631..aaa7fb1 100644
--- a/sys/fs/nfs/nfs.h
+++ b/sys/fs/nfs/nfs.h
@@ -466,6 +466,7 @@ struct nfssockreq {
u_int32_t nr_prog;
u_int32_t nr_vers;
struct __rpc_client *nr_client;
+ AUTH *nr_auth;
};
/*
diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c
index 2ac9262..47e5a37 100644
--- a/sys/fs/nfs/nfs_commonkrpc.c
+++ b/sys/fs/nfs/nfs_commonkrpc.c
@@ -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_jukebox_delay, CTLFLAG_RW, &nfs3_jukebox_del
"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 secflavour, char *clnt_principal,
{
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 secflavour, char *clnt_principal,
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 nfsmount *nmp,
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, struct nfsmount *nmp,
*/
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, struct nfsmount *nmp,
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)
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 45a5133..863c418 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -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 mntflags)
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)
diff --git a/sys/kgssapi/gss_impl.c b/sys/kgssapi/gss_impl.c
index 365a876..172471a 100644
--- a/sys/kgssapi/gss_impl.c
+++ b/sys/kgssapi/gss_impl.c
@@ -286,6 +286,7 @@ kgssapi_modevent(module_t mod, int type, void *data)
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;
diff --git a/sys/rpc/rpcsec_gss.h b/sys/rpc/rpcsec_gss.h
index 94696f3..7466d60 100644
--- a/sys/rpc/rpcsec_gss.h
+++ b/sys/rpc/rpcsec_gss.h
@@ -153,9 +153,9 @@ typedef AUTH *rpc_gss_secfind_ftype(CLIENT *clnt, struct ucred *cred,
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_name_ftype(rpc_gss_principal_t *principal,
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 svc_req *req, int max_tp_unit_len)
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);
diff --git a/sys/rpc/rpcsec_gss/rpcsec_gss.c b/sys/rpc/rpcsec_gss/rpcsec_gss.c
index ec0c595..c8a84eb 100644
--- a/sys/rpc/rpcsec_gss/rpcsec_gss.c
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss.c
@@ -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 ucred *cred, const char *principal,
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, struct ucred *cred, const char *principal,
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_ret_t *options_ret)
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_ret_t *options_ret)
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);
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 72bacce..b33049e 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -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,
OpenPOWER on IntegriCloud