fix nfs server handling of principal names
Subsystems (3)
cover fix nfs server handling of principal names
From: "J. Bruce Fields" <[email protected]>
Thanks to James Ralston for helping debug a problem with the server's handling of kerberos principal names, which could cause 4.1 mount failures when using Active Directory.
Along the way I noticed another bug and some opportunities for minor cleanup.
--b.
J. Bruce Fields (5):
svcrpc: move some initialization to common code
nfsd: helper for dup of possibly NULL string
nfsd: minor consolidation of mach_cred handling code
nfsd: fix unlikely NULL deref in mach_creds_match
nfsd4: fix gss-proxy 4.1 mounts for some AD principals
fs/nfsd/nfs4state.c | 55 +++++++++++++++++++++++++++--------- include/linux/sunrpc/svcauth.h | 9 +++++- net/sunrpc/auth_gss/gss_rpc_upcall.c | 3 ++ net/sunrpc/svcauth.c | 2 ++ net/sunrpc/svcauth_unix.c | 8 ------ 5 files changed, 55 insertions(+), 22 deletions(-)
Signature · 1 line
2.5.0
PATCH 1/5 svcrpc: move some initialization to common code
From: "J. Bruce Fields" <[email protected]>
Minor cleanup, no change in behavior.
--- net/sunrpc/svcauth.c | 2 ++ net/sunrpc/svcauth_unix.c | 8 -------- 2 files changed, 2 insertions(+), 8 deletions(-)
net/sunrpc/svcauth.c +2 -0
@@ -55,6 +55,7 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)spin_unlock (&authtab_lock); rqstp->rq_auth_slack =0 ; +init_svc_cred (&rqstp->rq_cred); rqstp->rq_authop = aops;return aops->accept (rqstp, authp);
@@ -63,6 +64,7 @@ EXPORT_SYMBOL_GPL(svc_authenticate);int svc_set_client (struct svc_rqst *rqstp) { + rqstp->rq_client =NULL ;return rqstp->rq_authop->set_client (rqstp); }EXPORT_SYMBOL_GPL (svc_set_client);
net/sunrpc/svcauth_unix.c +0 -8
@@ -728,10 +728,6 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)struct kvec *resv = &rqstp->rq_res.head[0 ];struct svc_cred *cred = &rqstp->rq_cred; - cred->cr_group_info =NULL ; - cred->cr_principal =NULL ; - rqstp->rq_client =NULL ; -if (argv->iov_len <3 *4 )return SVC_GARBAGE ;
@@ -794,10 +790,6 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)u32 slen, i;int len = argv->iov_len; - cred->cr_group_info =NULL ; - cred->cr_principal =NULL ; - rqstp->rq_client =NULL ; -if ((len -=3 *4 ) <0 )return SVC_GARBAGE ;
Signature · 1 line
2.5.0
PATCH 2/5 nfsd: helper for dup of possibly NULL string
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
Technically the initialization in the NULL case isn't even needed as the only caller already has target zeroed out, but it seems safer to keep copy_cred generic.
--- fs/nfsd/nfs4state.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-)
fs/nfsd/nfs4state.c +15 -6
@@ -1857,15 +1857,24 @@ static void copy_clid(struct nfs4_client *target, struct nfs4_client *source) target->cl_clientid.cl_id = source->cl_clientid.cl_id; } -static int copy_cred (struct svc_cred *target,struct svc_cred *source) +int strdup_if_nonnull (char **target,char *source) { -if (source->cr_principal) { - target->cr_principal = -kstrdup (source->cr_principal,GFP_KERNEL ); -if (target->cr_principal ==NULL ) +if (source) { + *target =kstrdup (source,GFP_KERNEL ); +if (!*target)return -ENOMEM ; }else - target->cr_principal =NULL ; + *target =NULL ; +return 0 ; +} + +static int copy_cred (struct svc_cred *target,struct svc_cred *source) +{ +int ret; + + ret =strdup_if_nonnull (&target->cr_principal, source->cr_principal); +if (ret) +return ret; target->cr_flavor = source->cr_flavor; target->cr_uid = source->cr_uid; target->cr_gid = source->cr_gid;
Signature · 1 line
2.5.0
PATCH 3/5 nfsd: minor consolidation of mach_cred handling code
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
Minor cleanup, no change in functionality.
--- fs/nfsd/nfs4state.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
fs/nfsd/nfs4state.c +10 -7
@@ -2374,10 +2374,17 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,if (exid->flags & ~EXCHGID4_FLAG_MASK_A )return nfserr_inval; + new =create_client (exid->clname, rqstp, &verf); +if (new ==NULL ) +return nfserr_jukebox; +switch (exid->spa_how) {case SP4_MACH_CRED : -if (!svc_rqst_integrity_protected (rqstp)) -return nfserr_inval; +if (!svc_rqst_integrity_protected (rqstp)) { + status = nfserr_inval; + goto out_nolock; + } + new->cl_mach_cred = true;case SP4_NONE :break ;default :/* checked by xdr code */
@@ -2386,10 +2393,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,return nfserr_encr_alg_unsupp; } - new =create_client (exid->clname, rqstp, &verf); -if (new ==NULL ) -return nfserr_jukebox; -/* Cases below refer to rfc 5661 section 18.35.4: */ spin_lock (&nn->client_lock); conf =find_confirmed_client_by_name (&exid->clname, nn);
@@ -2451,7 +2454,6 @@ out_new: goto out; } new->cl_minorversion = cstate->minorversion; - new->cl_mach_cred = (exid->spa_how ==SP4_MACH_CRED );gen_clid (new, nn);add_to_unconfirmed (new);
@@ -2469,6 +2471,7 @@ out_copy: out:spin_unlock (&nn->client_lock); +out_nolock:if (new)expire_client (new);if (unconf)
Signature · 1 line
2.5.0
PATCH 4/5 nfsd: fix unlikely NULL deref in mach_creds_match
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
We really shouldn't allow a client to be created with cl_mach_cred set unless it also has a principal name.
This also allows us to fail such cases immediately on EXCHANGE_ID as opposed to waiting and incorrectly returning WRONG_CRED on the following CREATE_SESSION.
--- fs/nfsd/nfs4state.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/nfsd/nfs4state.c +9 -0
@@ -2384,6 +2384,15 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, status = nfserr_inval; goto out_nolock; } +/* +* Sometimes userspace doesn't give us a principal. +* Which is a bug, really. Anyway, we can't enforce +* MACH_CRED in that case, better to give up now: +*/ +if (!new->cl_cred.cr_principal) { + status = nfserr_serverfault; + goto out_nolock; + } new->cl_mach_cred = true;case SP4_NONE :break ;
Signature · 1 line
2.5.0
PATCH 5/5 nfsd4: fix gss-proxy 4.1 mounts for some AD principals
In reply to J. Bruce FieldsFrom: "J. Bruce Fields" <[email protected]>
The principal name on a gss cred is used to setup the NFSv4.0 callback, which has to have a client principal name to authenticate to.
That code wants the name to be in the form servicetype@hostname. rpc.svcgssd passes down such names (and passes down no principal name at all in the case the principal isn't a service principal).
gss-proxy always passes down the principal name, and passes it down in the form servicetype/hostname@REALM. So we've been munging the name gss-proxy passes down into the format the NFSv4.0 callback code expects, or throwing away the name if we can't.
Since the introduction of the MACH_CRED enforcement in NFSv4.1, we've also been using the principal name to verify that certain operations are done as the same principal as was used on the original EXCHANGE_ID call.
For that application, the original name passed down by gss-proxy is also useful.
Lack of that name in some cases was causing some kerberized NFSv4.1 mount failures in an Active Directory environment.
This fix only works in the gss-proxy case. The fix for legacy rpc.svcgssd would be more involved, and rpc.svcgssd already has other problems in the AD case.
--- fs/nfsd/nfs4state.c | 10 +++++++++- include/linux/sunrpc/svcauth.h | 9 ++++++++- net/sunrpc/auth_gss/gss_rpc_upcall.c | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +9 -1
@@ -1875,6 +1875,10 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) ret =strdup_if_nonnull (&target->cr_principal, source->cr_principal);if (ret)return ret; + ret =strdup_if_nonnull (&target->cr_raw_principal, + source->cr_raw_principal); +if (ret) +return ret; target->cr_flavor = source->cr_flavor; target->cr_uid = source->cr_uid; target->cr_gid = source->cr_gid;
@@ -1978,6 +1982,9 @@ static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)return false;if (!svc_rqst_integrity_protected (rqstp))return false; +if (cl->cl_cred.cr_raw_principal) +return 0 ==strcmp (cl->cl_cred.cr_raw_principal, + cr->cr_raw_principal);if (!cr->cr_principal)return false;return 0 ==strcmp (cl->cl_cred.cr_principal, cr->cr_principal);
@@ -2389,7 +2396,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,* Which is a bug, really. Anyway, we can't enforce * MACH_CRED in that case, better to give up now: */ -if (!new->cl_cred.cr_principal) { +if (!new->cl_cred.cr_principal && + !new->cl_cred.cr_raw_principal) { status = nfserr_serverfault; goto out_nolock; }
include/linux/sunrpc/svcauth.h +8 -1
@@ -23,13 +23,19 @@ struct svc_cred {kgid_t cr_gid;struct group_info *cr_group_info;u32 cr_flavor;/* pseudoflavor */ -char *cr_principal;/* for gss */ +/* name of form servicetype/hostname@REALM, passed down by +* gss-proxy: */ +char *cr_raw_principal; +/* name of form servicetype@hostname, passed down by +* rpc.svcgssd, or computed from the above: */ +char *cr_principal;struct gss_api_mech *cr_gss_mech; };static inline void init_svc_cred (struct svc_cred *cred) { cred->cr_group_info =NULL ; + cred->cr_raw_principal =NULL ; cred->cr_principal =NULL ; cred->cr_gss_mech =NULL ; }
@@ -38,6 +44,7 @@ static inline void free_svc_cred(struct svc_cred *cred) {if (cred->cr_group_info)put_group_info (cred->cr_group_info); +kfree (cred->cr_raw_principal);kfree (cred->cr_principal);gss_mech_put (cred->cr_gss_mech);init_svc_cred (cred);
net/sunrpc/auth_gss/gss_rpc_upcall.c +3 -0
@@ -326,6 +326,9 @@ int gssp_accept_sec_context_upcall(struct net *net,if (data->found_creds && client_name.data !=NULL ) {char *c; + data->creds.cr_raw_principal =kstrndup (client_name.data, + client_name.len,GFP_KERNEL ); + data->creds.cr_principal =kstrndup (client_name.data, client_name.len,GFP_KERNEL );if (data->creds.cr_principal) {
Signature · 1 line
2.5.0
Re: [PATCH 5/5] nfsd4: fix gss-proxy 4.1 mounts for some AD principals
Quoted 24 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
The principal name on a gss cred is used to setup the NFSv4.0 callback, which has to have a client principal name to authenticate to.
That code wants the name to be in the form servicetype@hostname. rpc.svcgssd passes down such names (and passes down no principal name at all in the case the principal isn't a service principal).
gss-proxy always passes down the principal name, and passes it down in the form servicetype/hostname@REALM. So we've been munging the name gss-proxy passes down into the format the NFSv4.0 callback code expects, or throwing away the name if we can't.
Since the introduction of the
MACH_CREDenforcement in NFSv4.1, we've also been using the principal name to verify that certain operations are done as the same principal as was used on the originalEXCHANGE_IDcall.For that application, the original name passed down by gss-proxy is also useful.
Lack of that name in some cases was causing some kerberized NFSv4.1 mount failures in an Active Directory environment.
Quoted 6 lines
This fix only works in the gss-proxy case. The fix for legacy rpc.svcgssd would be more involved, and rpc.svcgssd already has other problems in the AD case.
Reported-and-tested-by James Ralston <[email protected]>Cc Simo Sorce <[email protected]>
LGTM, feel free to add ack by me.
Quoted 89 lines
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4state.c | 10 +++++++++- include/linux/sunrpc/svcauth.h | 9 ++++++++- net/sunrpc/auth_gss/gss_rpc_upcall.c | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-)fs/nfsd/nfs4state.c +9
-1@@ -1875,6 +1875,10 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) ret =strdup_if_nonnull (&target->cr_principal, source->cr_principal);if (ret)return ret; + ret =strdup_if_nonnull (&target->cr_raw_principal, + source->cr_raw_principal); +if (ret) +return ret; target->cr_flavor = source->cr_flavor; target->cr_uid = source->cr_uid; target->cr_gid = source->cr_gid;@@ -1978,6 +1982,9 @@ static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)return false;if (!svc_rqst_integrity_protected (rqstp))return false; +if (cl->cl_cred.cr_raw_principal) +return 0 ==strcmp (cl->cl_cred.cr_raw_principal, + cr->cr_raw_principal);if (!cr->cr_principal)return false;return 0 ==strcmp (cl->cl_cred.cr_principal, cr->cr_principal);@@ -2389,7 +2396,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,* Which is a bug, really. Anyway, we can't enforce * MACH_CRED in that case, better to give up now: */ -if (!new->cl_cred.cr_principal) { +if (!new->cl_cred.cr_principal && + !new->cl_cred.cr_raw_principal) { status = nfserr_serverfault; goto out_nolock; }include/linux/sunrpc/svcauth.h +8
-1@@ -23,13 +23,19 @@ struct svc_cred {kgid_t cr_gid;struct group_info *cr_group_info;u32 cr_flavor;/* pseudoflavor */ -char *cr_principal;/* for gss */ +/* name of form servicetype/hostname@REALM, passed down by +* gss-proxy: */ +char *cr_raw_principal; +/* name of form servicetype@hostname, passed down by +* rpc.svcgssd, or computed from the above: */ +char *cr_principal;struct gss_api_mech *cr_gss_mech; };static inline void init_svc_cred (struct svc_cred *cred) { cred->cr_group_info =NULL ; + cred->cr_raw_principal =NULL ; cred->cr_principal =NULL ; cred->cr_gss_mech =NULL ; }@@ -38,6 +44,7 @@ static inline void free_svc_cred(struct svc_cred *cred) {if (cred->cr_group_info)put_group_info (cred->cr_group_info); +kfree (cred->cr_raw_principal);kfree (cred->cr_principal);gss_mech_put (cred->cr_gss_mech);init_svc_cred (cred);net/sunrpc/auth_gss/gss_rpc_upcall.c +3
-0@@ -326,6 +326,9 @@ int gssp_accept_sec_context_upcall(struct net *net,if (data->found_creds && client_name.data !=NULL ) {char *c; + data->creds.cr_raw_principal =kstrndup (client_name.data, + client_name.len,GFP_KERNEL ); + data->creds.cr_principal =kstrndup (client_name.data, client_name.len,GFP_KERNEL );if (data->creds.cr_principal) {
Signature · 1 line
Simo Sorce * Red Hat, Inc * New York
Re: [PATCH 5/5] nfsd4: fix gss-proxy 4.1 mounts for some AD principals
Quoted 25 lines · Simo Sorce
Quoted 24 lines · J. Bruce Fields
From: "J. Bruce Fields" <[email protected]>
The principal name on a gss cred is used to setup the NFSv4.0 callback, which has to have a client principal name to authenticate to.
That code wants the name to be in the form servicetype@hostname. rpc.svcgssd passes down such names (and passes down no principal name at all in the case the principal isn't a service principal).
gss-proxy always passes down the principal name, and passes it down in the form servicetype/hostname@REALM. So we've been munging the name gss-proxy passes down into the format the NFSv4.0 callback code expects, or throwing away the name if we can't.
Since the introduction of the
MACH_CREDenforcement in NFSv4.1, we've also been using the principal name to verify that certain operations are done as the same principal as was used on the originalEXCHANGE_IDcall.For that application, the original name passed down by gss-proxy is also useful.
Lack of that name in some cases was causing some kerberized NFSv4.1 mount failures in an Active Directory environment.
Quoted 8 lines
Quoted 6 lines
This fix only works in the gss-proxy case. The fix for legacy rpc.svcgssd would be more involved, and rpc.svcgssd already has other problems in the AD case.
Reported-and-tested-by James Ralston <[email protected]>Cc Simo Sorce <[email protected]>LGTM, feel free to add ack by me.
Thanks!--b.
Quoted 95 lines
Quoted 90 lines
Signed-off-by J. Bruce Fields <[email protected]>--- fs/nfsd/nfs4state.c | 10 +++++++++- include/linux/sunrpc/svcauth.h | 9 ++++++++- net/sunrpc/auth_gss/gss_rpc_upcall.c | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-)fs/nfsd/nfs4state.c +9
-1@@ -1875,6 +1875,10 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) ret =strdup_if_nonnull (&target->cr_principal, source->cr_principal);if (ret)return ret; + ret =strdup_if_nonnull (&target->cr_raw_principal, + source->cr_raw_principal); +if (ret) +return ret; target->cr_flavor = source->cr_flavor; target->cr_uid = source->cr_uid; target->cr_gid = source->cr_gid;@@ -1978,6 +1982,9 @@ static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)return false;if (!svc_rqst_integrity_protected (rqstp))return false; +if (cl->cl_cred.cr_raw_principal) +return 0 ==strcmp (cl->cl_cred.cr_raw_principal, + cr->cr_raw_principal);if (!cr->cr_principal)return false;return 0 ==strcmp (cl->cl_cred.cr_principal, cr->cr_principal);@@ -2389,7 +2396,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,* Which is a bug, really. Anyway, we can't enforce * MACH_CRED in that case, better to give up now: */ -if (!new->cl_cred.cr_principal) { +if (!new->cl_cred.cr_principal && + !new->cl_cred.cr_raw_principal) { status = nfserr_serverfault; goto out_nolock; }include/linux/sunrpc/svcauth.h +8
-1@@ -23,13 +23,19 @@ struct svc_cred {kgid_t cr_gid;struct group_info *cr_group_info;u32 cr_flavor;/* pseudoflavor */ -char *cr_principal;/* for gss */ +/* name of form servicetype/hostname@REALM, passed down by +* gss-proxy: */ +char *cr_raw_principal; +/* name of form servicetype@hostname, passed down by +* rpc.svcgssd, or computed from the above: */ +char *cr_principal;struct gss_api_mech *cr_gss_mech; };static inline void init_svc_cred (struct svc_cred *cred) { cred->cr_group_info =NULL ; + cred->cr_raw_principal =NULL ; cred->cr_principal =NULL ; cred->cr_gss_mech =NULL ; }@@ -38,6 +44,7 @@ static inline void free_svc_cred(struct svc_cred *cred) {if (cred->cr_group_info)put_group_info (cred->cr_group_info); +kfree (cred->cr_raw_principal);kfree (cred->cr_principal);gss_mech_put (cred->cr_gss_mech);init_svc_cred (cred);net/sunrpc/auth_gss/gss_rpc_upcall.c +3
-0@@ -326,6 +326,9 @@ int gssp_accept_sec_context_upcall(struct net *net,if (data->found_creds && client_name.data !=NULL ) {char *c; + data->creds.cr_raw_principal =kstrndup (client_name.data, + client_name.len,GFP_KERNEL ); + data->creds.cr_principal =kstrndup (client_name.data, client_name.len,GFP_KERNEL );if (data->creds.cr_principal) {Signature · 1 line
Simo Sorce * Red Hat, Inc * New York