mirror of
https://github.com/freebsd/freebsd-src
synced 2024-07-23 19:28:36 +00:00
nfscl/kgssapi: Fix Kerberized NFS mounts to pNFS servers
During recent testing related to the IETF NFSv4 Bakeathon, it was discovered that Kerberized NFSv4.1/4.2 mounts to pNFS servers (sec=krb5[ip],pnfs mount options) was broken. The FreeBSD client was using the "service principal" for the MDS to try and establish a rpcsec_gss credential for a DS, which is incorrect. (A "service principal" looks like "nfs@<fqdn-of-server>" and the <fqdn-of-server> for the DS is not the same as the MDS for most pNFS servers.) To fix this, the rpcsec_gss code needs to be able to do a reverse DNS lookup of the DS's IP address. A new kgssapi upcall to the gssd(8) daemon is added by this patch to do the reverse DNS along with a new rpcsec_gss function to generate the "service principal". A separate patch to the gssd(8) will be committed, so that this patch will fix the problem. Without the gssd(8) patch, the new upcall fails and current/incorrect behaviour remains. This bug only affects the rare case of a Kerberized (sec=krb5[ip],pnfs) mount using pNFS. This patch changes the internal KAPI between the kgssapi and nfscl modules, but since I did a version bump a few days ago, I will not do one this time. MFC after: 1 month
This commit is contained in:
parent
428879dc91
commit
dd7d42a1fa
|
@ -3983,6 +3983,7 @@ kgssapi/gss_get_mic.c optional kgssapi
|
||||||
kgssapi/gss_init_sec_context.c optional kgssapi
|
kgssapi/gss_init_sec_context.c optional kgssapi
|
||||||
kgssapi/gss_impl.c optional kgssapi
|
kgssapi/gss_impl.c optional kgssapi
|
||||||
kgssapi/gss_import_name.c optional kgssapi
|
kgssapi/gss_import_name.c optional kgssapi
|
||||||
|
kgssapi/gss_ip_to_dns.c optional kgssapi
|
||||||
kgssapi/gss_names.c optional kgssapi
|
kgssapi/gss_names.c optional kgssapi
|
||||||
kgssapi/gss_pname_to_uid.c optional kgssapi
|
kgssapi/gss_pname_to_uid.c optional kgssapi
|
||||||
kgssapi/gss_release_buffer.c optional kgssapi
|
kgssapi/gss_release_buffer.c optional kgssapi
|
||||||
|
|
|
@ -636,6 +636,7 @@ struct nfssockreq {
|
||||||
u_int32_t nr_vers;
|
u_int32_t nr_vers;
|
||||||
struct __rpc_client *nr_client;
|
struct __rpc_client *nr_client;
|
||||||
AUTH *nr_auth;
|
AUTH *nr_auth;
|
||||||
|
char nr_srvprinc[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -599,10 +599,14 @@ nfs_getauth(struct nfssockreq *nrp, int secflavour, char *clnt_principal,
|
||||||
else
|
else
|
||||||
svc = rpc_gss_svc_privacy;
|
svc = rpc_gss_svc_privacy;
|
||||||
|
|
||||||
if (clnt_principal == NULL)
|
if (clnt_principal == NULL) {
|
||||||
|
NFSCL_DEBUG(1, "nfs_getauth: clnt princ=NULL, "
|
||||||
|
"srv princ=%s\n", srv_principal);
|
||||||
auth = rpc_gss_secfind_call(nrp->nr_client, cred,
|
auth = rpc_gss_secfind_call(nrp->nr_client, cred,
|
||||||
srv_principal, mech_oid, svc);
|
srv_principal, mech_oid, svc);
|
||||||
else {
|
} else {
|
||||||
|
NFSCL_DEBUG(1, "nfs_getauth: clnt princ=%s "
|
||||||
|
"srv princ=%s\n", clnt_principal, srv_principal);
|
||||||
auth = rpc_gss_seccreate_call(nrp->nr_client, cred,
|
auth = rpc_gss_seccreate_call(nrp->nr_client, cred,
|
||||||
clnt_principal, srv_principal, "kerberosv5",
|
clnt_principal, srv_principal, "kerberosv5",
|
||||||
svc, NULL, NULL, NULL);
|
svc, NULL, NULL, NULL);
|
||||||
|
@ -799,7 +803,10 @@ newnfs_request(struct nfsrv_descript *nd, struct nfsmount *nmp,
|
||||||
secflavour = RPCSEC_GSS_KRB5P;
|
secflavour = RPCSEC_GSS_KRB5P;
|
||||||
else
|
else
|
||||||
secflavour = RPCSEC_GSS_KRB5;
|
secflavour = RPCSEC_GSS_KRB5;
|
||||||
srv_principal = NFSMNT_SRVKRBNAME(nmp);
|
if (nrp->nr_srvprinc[0] == '\0')
|
||||||
|
srv_principal = NFSMNT_SRVKRBNAME(nmp);
|
||||||
|
else
|
||||||
|
srv_principal = nrp->nr_srvprinc;
|
||||||
} else if (nmp != NULL && (!NFSHASKERB(nmp) || NFSHASSYSKRB5(nmp)) &&
|
} else if (nmp != NULL && (!NFSHASKERB(nmp) || NFSHASSYSKRB5(nmp)) &&
|
||||||
nd->nd_procnum != NFSPROC_NULL &&
|
nd->nd_procnum != NFSPROC_NULL &&
|
||||||
(nd->nd_flag & ND_USEGSSNAME) != 0) {
|
(nd->nd_flag & ND_USEGSSNAME) != 0) {
|
||||||
|
|
|
@ -6014,7 +6014,27 @@ nfsrpc_fillsa(struct nfsmount *nmp, struct sockaddr_in *sin,
|
||||||
sad->sin_family = AF_INET;
|
sad->sin_family = AF_INET;
|
||||||
sad->sin_port = sin->sin_port;
|
sad->sin_port = sin->sin_port;
|
||||||
sad->sin_addr.s_addr = sin->sin_addr.s_addr;
|
sad->sin_addr.s_addr = sin->sin_addr.s_addr;
|
||||||
nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ, M_WAITOK | M_ZERO);
|
if (NFSHASPNFS(nmp) && NFSHASKERB(nmp)) {
|
||||||
|
/* For pNFS, a separate server principal is needed. */
|
||||||
|
nrp = malloc(sizeof(*nrp) + NI_MAXSERV + NI_MAXHOST,
|
||||||
|
M_NFSSOCKREQ, M_WAITOK | M_ZERO);
|
||||||
|
/*
|
||||||
|
* Use the latter part of nr_srvprinc as a temporary
|
||||||
|
* buffer for the IP address.
|
||||||
|
*/
|
||||||
|
inet_ntoa_r(sad->sin_addr,
|
||||||
|
&nrp->nr_srvprinc[NI_MAXSERV]);
|
||||||
|
NFSCL_DEBUG(1, "nfsrpc_fillsa: DS IP=%s\n",
|
||||||
|
&nrp->nr_srvprinc[NI_MAXSERV]);
|
||||||
|
if (!rpc_gss_ip_to_srv_principal_call(
|
||||||
|
&nrp->nr_srvprinc[NI_MAXSERV], "nfs",
|
||||||
|
nrp->nr_srvprinc))
|
||||||
|
nrp->nr_srvprinc[0] = '\0';
|
||||||
|
NFSCL_DEBUG(1, "nfsrpc_fillsa: srv principal=%s\n",
|
||||||
|
nrp->nr_srvprinc);
|
||||||
|
} else
|
||||||
|
nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ,
|
||||||
|
M_WAITOK | M_ZERO);
|
||||||
nrp->nr_nam = (struct sockaddr *)sad;
|
nrp->nr_nam = (struct sockaddr *)sad;
|
||||||
} else if (af == AF_INET6) {
|
} else if (af == AF_INET6) {
|
||||||
NFSLOCKMNT(nmp);
|
NFSLOCKMNT(nmp);
|
||||||
|
@ -6053,7 +6073,27 @@ nfsrpc_fillsa(struct nfsmount *nmp, struct sockaddr_in *sin,
|
||||||
sad6->sin6_port = sin6->sin6_port;
|
sad6->sin6_port = sin6->sin6_port;
|
||||||
NFSBCOPY(&sin6->sin6_addr, &sad6->sin6_addr,
|
NFSBCOPY(&sin6->sin6_addr, &sad6->sin6_addr,
|
||||||
sizeof(struct in6_addr));
|
sizeof(struct in6_addr));
|
||||||
nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ, M_WAITOK | M_ZERO);
|
if (NFSHASPNFS(nmp) && NFSHASKERB(nmp)) {
|
||||||
|
/* For pNFS, a separate server principal is needed. */
|
||||||
|
nrp = malloc(sizeof(*nrp) + NI_MAXSERV + NI_MAXHOST,
|
||||||
|
M_NFSSOCKREQ, M_WAITOK | M_ZERO);
|
||||||
|
/*
|
||||||
|
* Use the latter part of nr_srvprinc as a temporary
|
||||||
|
* buffer for the IP address.
|
||||||
|
*/
|
||||||
|
inet_ntop(AF_INET6, &sad6->sin6_addr,
|
||||||
|
&nrp->nr_srvprinc[NI_MAXSERV], NI_MAXHOST);
|
||||||
|
NFSCL_DEBUG(1, "nfsrpc_fillsa: DS IP=%s\n",
|
||||||
|
&nrp->nr_srvprinc[NI_MAXSERV]);
|
||||||
|
if (!rpc_gss_ip_to_srv_principal_call(
|
||||||
|
&nrp->nr_srvprinc[NI_MAXSERV], "nfs",
|
||||||
|
nrp->nr_srvprinc))
|
||||||
|
nrp->nr_srvprinc[0] = '\0';
|
||||||
|
NFSCL_DEBUG(1, "nfsrpc_fillsa: srv principal=%s\n",
|
||||||
|
nrp->nr_srvprinc);
|
||||||
|
} else
|
||||||
|
nrp = malloc(sizeof(*nrp), M_NFSSOCKREQ,
|
||||||
|
M_WAITOK | M_ZERO);
|
||||||
nrp->nr_nam = (struct sockaddr *)sad6;
|
nrp->nr_nam = (struct sockaddr *)sad6;
|
||||||
} else
|
} else
|
||||||
return (EPERM);
|
return (EPERM);
|
||||||
|
|
|
@ -338,6 +338,8 @@ kgssapi_modevent(module_t mod, int type, void *data)
|
||||||
rpc_gss_get_principal_name;
|
rpc_gss_get_principal_name;
|
||||||
rpc_gss_entries.rpc_gss_svc_max_data_length =
|
rpc_gss_entries.rpc_gss_svc_max_data_length =
|
||||||
rpc_gss_svc_max_data_length;
|
rpc_gss_svc_max_data_length;
|
||||||
|
rpc_gss_entries.rpc_gss_ip_to_srv_principal =
|
||||||
|
rpc_gss_ip_to_srv_principal;
|
||||||
mtx_init(&kgss_gssd_lock, "kgss_gssd_lock", NULL, MTX_DEF);
|
mtx_init(&kgss_gssd_lock, "kgss_gssd_lock", NULL, MTX_DEF);
|
||||||
error = kgss_load();
|
error = kgss_load();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -372,6 +372,18 @@ extern gss_OID GSS_KRB5_NT_STRING_UID_NAME;
|
||||||
#define GSS_S_GAP_TOKEN \
|
#define GSS_S_GAP_TOKEN \
|
||||||
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
|
(1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NI_MAXSERV and NI_MAXHOST. The srv_principal argument for
|
||||||
|
* rpc_gss_ip_to_srv_principal should point to at least
|
||||||
|
* NI_MAXSERV + NI_MAXHOST + 1 bytes of storage. The "+ 1" is for the '@'.
|
||||||
|
* The NI_MAXHOST limit is checked for gss_ip_to_dns().
|
||||||
|
* These should be set to the same value as they are in <netdb.h>.
|
||||||
|
*/
|
||||||
|
#ifndef NI_MAXHOST
|
||||||
|
#define NI_MAXSERV 32
|
||||||
|
#define NI_MAXHOST 1025
|
||||||
|
#endif
|
||||||
|
|
||||||
__BEGIN_DECLS
|
__BEGIN_DECLS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -568,6 +580,12 @@ OM_uint32 gss_pname_to_unix_cred
|
||||||
gid_t *groups /* pointer to group list */
|
gid_t *groups /* pointer to group list */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
OM_uint32 gss_ip_to_dns
|
||||||
|
(OM_uint32 *, /* minor status */
|
||||||
|
char *ip_addr, /* IP host address string */
|
||||||
|
char *dns_name /* pointer to dns_name for result */
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mbuf oriented message signing and encryption.
|
* Mbuf oriented message signing and encryption.
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
%#include <kgssapi/gssapi.h>
|
%#include <kgssapi/gssapi.h>
|
||||||
%#else
|
%#else
|
||||||
%#include <gssapi/gssapi.h>
|
%#include <gssapi/gssapi.h>
|
||||||
|
%#include <netdb.h>
|
||||||
%#endif
|
%#endif
|
||||||
|
|
||||||
%extern bool_t xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *buf);
|
%extern bool_t xdr_gss_buffer_desc(XDR *xdrs, gss_buffer_desc *buf);
|
||||||
|
@ -218,6 +219,16 @@ struct display_status_args {
|
||||||
uint32_t message_context;
|
uint32_t message_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ip_to_dns_res {
|
||||||
|
uint32_t major_status;
|
||||||
|
uint32_t minor_status;
|
||||||
|
char dns_name<NI_MAXHOST>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ip_to_dns_args {
|
||||||
|
char ip_addr<NI_MAXHOST>;
|
||||||
|
};
|
||||||
|
|
||||||
program GSSD {
|
program GSSD {
|
||||||
version GSSDVERS {
|
version GSSDVERS {
|
||||||
void GSSD_NULL(void) = 0;
|
void GSSD_NULL(void) = 0;
|
||||||
|
@ -260,5 +271,8 @@ program GSSD {
|
||||||
|
|
||||||
display_status_res
|
display_status_res
|
||||||
GSSD_DISPLAY_STATUS(display_status_args) = 13;
|
GSSD_DISPLAY_STATUS(display_status_args) = 13;
|
||||||
|
|
||||||
|
ip_to_dns_res
|
||||||
|
GSSD_IP_TO_DNS(ip_to_dns_args) = 14;
|
||||||
} = 1;
|
} = 1;
|
||||||
} = 0x40677373;
|
} = 0x40677373;
|
||||||
|
|
|
@ -14,6 +14,7 @@ SRCS= gss_accept_sec_context.c \
|
||||||
gss_init_sec_context.c \
|
gss_init_sec_context.c \
|
||||||
gss_impl.c \
|
gss_impl.c \
|
||||||
gss_import_name.c \
|
gss_import_name.c \
|
||||||
|
gss_ip_to_dns.c \
|
||||||
gss_names.c \
|
gss_names.c \
|
||||||
gss_pname_to_uid.c \
|
gss_pname_to_uid.c \
|
||||||
gss_release_buffer.c \
|
gss_release_buffer.c \
|
||||||
|
|
|
@ -184,6 +184,8 @@ typedef bool_t rpc_gss_get_principal_name_ftype(rpc_gss_principal_t *principal,
|
||||||
typedef int rpc_gss_svc_max_data_length_ftype(struct svc_req *req,
|
typedef int rpc_gss_svc_max_data_length_ftype(struct svc_req *req,
|
||||||
int max_tp_unit_len);
|
int max_tp_unit_len);
|
||||||
typedef void rpc_gss_refresh_auth_ftype(AUTH *auth);
|
typedef void rpc_gss_refresh_auth_ftype(AUTH *auth);
|
||||||
|
typedef bool_t rpc_gss_ip_to_srv_principal_ftype(char *ip_addr,
|
||||||
|
const char *srv_name, char *dns_name);
|
||||||
|
|
||||||
struct rpc_gss_entries {
|
struct rpc_gss_entries {
|
||||||
rpc_gss_secfind_ftype *rpc_gss_secfind;
|
rpc_gss_secfind_ftype *rpc_gss_secfind;
|
||||||
|
@ -206,6 +208,7 @@ struct rpc_gss_entries {
|
||||||
rpc_gss_get_principal_name_ftype *rpc_gss_get_principal_name;
|
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_svc_max_data_length_ftype *rpc_gss_svc_max_data_length;
|
||||||
rpc_gss_refresh_auth_ftype *rpc_gss_refresh_auth;
|
rpc_gss_refresh_auth_ftype *rpc_gss_refresh_auth;
|
||||||
|
rpc_gss_ip_to_srv_principal_ftype *rpc_gss_ip_to_srv_principal;
|
||||||
};
|
};
|
||||||
extern struct rpc_gss_entries rpc_gss_entries;
|
extern struct rpc_gss_entries rpc_gss_entries;
|
||||||
|
|
||||||
|
@ -417,6 +420,18 @@ rpc_gss_refresh_auth_call(AUTH *auth)
|
||||||
(*rpc_gss_entries.rpc_gss_refresh_auth)(auth);
|
(*rpc_gss_entries.rpc_gss_refresh_auth)(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __inline bool_t
|
||||||
|
rpc_gss_ip_to_srv_principal_call(char *ip_addr, const char *srv_name,
|
||||||
|
char *dns_name)
|
||||||
|
{
|
||||||
|
bool_t ret = FALSE;
|
||||||
|
|
||||||
|
if (rpc_gss_entries.rpc_gss_ip_to_srv_principal != NULL)
|
||||||
|
ret = (*rpc_gss_entries.rpc_gss_ip_to_srv_principal)(ip_addr,
|
||||||
|
srv_name, dns_name);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
AUTH *rpc_gss_secfind(CLIENT *clnt, struct ucred *cred,
|
AUTH *rpc_gss_secfind(CLIENT *clnt, struct ucred *cred,
|
||||||
const char *principal, gss_OID mech_oid, rpc_gss_service_t service);
|
const char *principal, gss_OID mech_oid, rpc_gss_service_t service);
|
||||||
void rpc_gss_secpurge(CLIENT *clnt);
|
void rpc_gss_secpurge(CLIENT *clnt);
|
||||||
|
@ -455,6 +470,8 @@ void rpc_gss_clear_callback(rpc_gss_callback_t *cb);
|
||||||
bool_t rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
|
bool_t rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
|
||||||
const char *mech, const char *name, const char *node, const char *domain);
|
const char *mech, const char *name, const char *node, const char *domain);
|
||||||
int rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len);
|
int rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len);
|
||||||
|
bool_t rpc_gss_ip_to_srv_principal(char *ip_addr, const char *srv_name,
|
||||||
|
char *dns_name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal interface from the RPC implementation.
|
* Internal interface from the RPC implementation.
|
||||||
|
|
|
@ -457,6 +457,37 @@ rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
|
||||||
return (TRUE);
|
return (TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that the ip_addr and srv_principal pointers can point to the same
|
||||||
|
* buffer, so long as ip_addr is at least strlen(srv_name) + 1 > srv_principal.
|
||||||
|
*/
|
||||||
|
bool_t
|
||||||
|
rpc_gss_ip_to_srv_principal(char *ip_addr, const char *srv_name,
|
||||||
|
char *srv_principal)
|
||||||
|
{
|
||||||
|
OM_uint32 maj_stat, min_stat;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First fill in the service name and '@'.
|
||||||
|
*/
|
||||||
|
len = strlen(srv_name);
|
||||||
|
if (len > NI_MAXSERV)
|
||||||
|
return (FALSE);
|
||||||
|
memcpy(srv_principal, srv_name, len);
|
||||||
|
srv_principal[len] = '@';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do reverse DNS to get the DNS name for the ip_addr.
|
||||||
|
*/
|
||||||
|
maj_stat = gss_ip_to_dns(&min_stat, ip_addr, &srv_principal[len + 1]);
|
||||||
|
if (maj_stat != GSS_S_COMPLETE) {
|
||||||
|
rpc_gss_log_status("gss_ip_to_dns", NULL, maj_stat, min_stat);
|
||||||
|
return (FALSE);
|
||||||
|
}
|
||||||
|
return (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
bool_t
|
bool_t
|
||||||
rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
|
rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
|
||||||
rpc_gss_ucred_t **ucred, void **cookie)
|
rpc_gss_ucred_t **ucred, void **cookie)
|
||||||
|
|
Loading…
Reference in a new issue