diff options
author | Weston Andros Adamson <dros@netapp.com> | 2013-08-13 16:37:32 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-09-05 10:40:45 -0400 |
commit | 2031cd1af143f47dacacbb35efbef22f2fd079e6 (patch) | |
tree | d5737cb0679b190fcfd583bb74768edfa032203e /fs/nfs | |
parent | 92cb6c5be8134db6f7c38f25f6afd13e444cebaf (diff) |
nfs4.1: Minimal SP4_MACH_CRED implementation
This is a minimal client side implementation of SP4_MACH_CRED. It will
attempt to negotiate SP4_MACH_CRED iff the EXCHANGE_ID is using
krb5i or krb5p auth. SP4_MACH_CRED will be used if the server supports the
minimal operations:
BIND_CONN_TO_SESSION
EXCHANGE_ID
CREATE_SESSION
DESTROY_SESSION
DESTROY_CLIENTID
This patch only includes the EXCHANGE_ID negotiation code because
the client will already use the machine cred for these operations.
If the server doesn't support SP4_MACH_CRED or doesn't support the minimal
operations, the exchange id will be resent with SP4_NONE.
Signed-off-by: Weston Andros Adamson <dros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/nfs4proc.c | 133 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 72 |
2 files changed, 188 insertions, 17 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 535011a27415..ab6ee1dffd7b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -6117,16 +6117,87 @@ out: | |||
6117 | } | 6117 | } |
6118 | 6118 | ||
6119 | /* | 6119 | /* |
6120 | * nfs4_proc_exchange_id() | 6120 | * Minimum set of SP4_MACH_CRED operations from RFC 5661 |
6121 | */ | ||
6122 | static const struct nfs41_state_protection nfs4_sp4_mach_cred_request = { | ||
6123 | .how = SP4_MACH_CRED, | ||
6124 | .enforce.u.words = { | ||
6125 | [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) | | ||
6126 | 1 << (OP_EXCHANGE_ID - 32) | | ||
6127 | 1 << (OP_CREATE_SESSION - 32) | | ||
6128 | 1 << (OP_DESTROY_SESSION - 32) | | ||
6129 | 1 << (OP_DESTROY_CLIENTID - 32) | ||
6130 | } | ||
6131 | }; | ||
6132 | |||
6133 | /* | ||
6134 | * Select the state protection mode for client `clp' given the server results | ||
6135 | * from exchange_id in `sp'. | ||
6121 | * | 6136 | * |
6122 | * Returns zero, a negative errno, or a negative NFS4ERR status code. | 6137 | * Returns 0 on success, negative errno otherwise. |
6138 | */ | ||
6139 | static int nfs4_sp4_select_mode(struct nfs_client *clp, | ||
6140 | struct nfs41_state_protection *sp) | ||
6141 | { | ||
6142 | static const u32 supported_enforce[NFS4_OP_MAP_NUM_WORDS] = { | ||
6143 | [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) | | ||
6144 | 1 << (OP_EXCHANGE_ID - 32) | | ||
6145 | 1 << (OP_CREATE_SESSION - 32) | | ||
6146 | 1 << (OP_DESTROY_SESSION - 32) | | ||
6147 | 1 << (OP_DESTROY_CLIENTID - 32) | ||
6148 | }; | ||
6149 | unsigned int i; | ||
6150 | |||
6151 | if (sp->how == SP4_MACH_CRED) { | ||
6152 | /* Print state protect result */ | ||
6153 | dfprintk(MOUNT, "Server SP4_MACH_CRED support:\n"); | ||
6154 | for (i = 0; i <= LAST_NFS4_OP; i++) { | ||
6155 | if (test_bit(i, sp->enforce.u.longs)) | ||
6156 | dfprintk(MOUNT, " enforce op %d\n", i); | ||
6157 | if (test_bit(i, sp->allow.u.longs)) | ||
6158 | dfprintk(MOUNT, " allow op %d\n", i); | ||
6159 | } | ||
6160 | |||
6161 | /* make sure nothing is on enforce list that isn't supported */ | ||
6162 | for (i = 0; i < NFS4_OP_MAP_NUM_WORDS; i++) { | ||
6163 | if (sp->enforce.u.words[i] & ~supported_enforce[i]) { | ||
6164 | dfprintk(MOUNT, "sp4_mach_cred: disabled\n"); | ||
6165 | return -EINVAL; | ||
6166 | } | ||
6167 | } | ||
6168 | |||
6169 | /* | ||
6170 | * Minimal mode - state operations are allowed to use machine | ||
6171 | * credential. Note this already happens by default, so the | ||
6172 | * client doesn't have to do anything more than the negotiation. | ||
6173 | * | ||
6174 | * NOTE: we don't care if EXCHANGE_ID is in the list - | ||
6175 | * we're already using the machine cred for exchange_id | ||
6176 | * and will never use a different cred. | ||
6177 | */ | ||
6178 | if (test_bit(OP_BIND_CONN_TO_SESSION, sp->enforce.u.longs) && | ||
6179 | test_bit(OP_CREATE_SESSION, sp->enforce.u.longs) && | ||
6180 | test_bit(OP_DESTROY_SESSION, sp->enforce.u.longs) && | ||
6181 | test_bit(OP_DESTROY_CLIENTID, sp->enforce.u.longs)) { | ||
6182 | dfprintk(MOUNT, "sp4_mach_cred:\n"); | ||
6183 | dfprintk(MOUNT, " minimal mode enabled\n"); | ||
6184 | set_bit(NFS_SP4_MACH_CRED_MINIMAL, &clp->cl_sp4_flags); | ||
6185 | } else { | ||
6186 | dfprintk(MOUNT, "sp4_mach_cred: disabled\n"); | ||
6187 | return -EINVAL; | ||
6188 | } | ||
6189 | } | ||
6190 | |||
6191 | return 0; | ||
6192 | } | ||
6193 | |||
6194 | /* | ||
6195 | * _nfs4_proc_exchange_id() | ||
6123 | * | 6196 | * |
6124 | * Since the clientid has expired, all compounds using sessions | 6197 | * Wrapper for EXCHANGE_ID operation. |
6125 | * associated with the stale clientid will be returning | ||
6126 | * NFS4ERR_BADSESSION in the sequence operation, and will therefore | ||
6127 | * be in some phase of session reset. | ||
6128 | */ | 6198 | */ |
6129 | int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) | 6199 | static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred, |
6200 | u32 sp4_how) | ||
6130 | { | 6201 | { |
6131 | nfs4_verifier verifier; | 6202 | nfs4_verifier verifier; |
6132 | struct nfs41_exchange_id_args args = { | 6203 | struct nfs41_exchange_id_args args = { |
@@ -6173,11 +6244,30 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) | |||
6173 | goto out_server_scope; | 6244 | goto out_server_scope; |
6174 | } | 6245 | } |
6175 | 6246 | ||
6247 | switch (sp4_how) { | ||
6248 | case SP4_NONE: | ||
6249 | args.state_protect.how = SP4_NONE; | ||
6250 | break; | ||
6251 | |||
6252 | case SP4_MACH_CRED: | ||
6253 | args.state_protect = nfs4_sp4_mach_cred_request; | ||
6254 | break; | ||
6255 | |||
6256 | default: | ||
6257 | /* unsupported! */ | ||
6258 | WARN_ON_ONCE(1); | ||
6259 | status = -EINVAL; | ||
6260 | goto out_server_scope; | ||
6261 | } | ||
6262 | |||
6176 | status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); | 6263 | status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); |
6177 | trace_nfs4_exchange_id(clp, status); | 6264 | trace_nfs4_exchange_id(clp, status); |
6178 | if (status == 0) | 6265 | if (status == 0) |
6179 | status = nfs4_check_cl_exchange_flags(res.flags); | 6266 | status = nfs4_check_cl_exchange_flags(res.flags); |
6180 | 6267 | ||
6268 | if (status == 0) | ||
6269 | status = nfs4_sp4_select_mode(clp, &res.state_protect); | ||
6270 | |||
6181 | if (status == 0) { | 6271 | if (status == 0) { |
6182 | clp->cl_clientid = res.clientid; | 6272 | clp->cl_clientid = res.clientid; |
6183 | clp->cl_exchange_flags = (res.flags & ~EXCHGID4_FLAG_CONFIRMED_R); | 6273 | clp->cl_exchange_flags = (res.flags & ~EXCHGID4_FLAG_CONFIRMED_R); |
@@ -6224,6 +6314,35 @@ out: | |||
6224 | return status; | 6314 | return status; |
6225 | } | 6315 | } |
6226 | 6316 | ||
6317 | /* | ||
6318 | * nfs4_proc_exchange_id() | ||
6319 | * | ||
6320 | * Returns zero, a negative errno, or a negative NFS4ERR status code. | ||
6321 | * | ||
6322 | * Since the clientid has expired, all compounds using sessions | ||
6323 | * associated with the stale clientid will be returning | ||
6324 | * NFS4ERR_BADSESSION in the sequence operation, and will therefore | ||
6325 | * be in some phase of session reset. | ||
6326 | * | ||
6327 | * Will attempt to negotiate SP4_MACH_CRED if krb5i / krb5p auth is used. | ||
6328 | */ | ||
6329 | int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) | ||
6330 | { | ||
6331 | rpc_authflavor_t authflavor = clp->cl_rpcclient->cl_auth->au_flavor; | ||
6332 | int status; | ||
6333 | |||
6334 | /* try SP4_MACH_CRED if krb5i/p */ | ||
6335 | if (authflavor == RPC_AUTH_GSS_KRB5I || | ||
6336 | authflavor == RPC_AUTH_GSS_KRB5P) { | ||
6337 | status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED); | ||
6338 | if (!status) | ||
6339 | return 0; | ||
6340 | } | ||
6341 | |||
6342 | /* try SP4_NONE */ | ||
6343 | return _nfs4_proc_exchange_id(clp, cred, SP4_NONE); | ||
6344 | } | ||
6345 | |||
6227 | static int _nfs4_proc_destroy_clientid(struct nfs_client *clp, | 6346 | static int _nfs4_proc_destroy_clientid(struct nfs_client *clp, |
6228 | struct rpc_cred *cred) | 6347 | struct rpc_cred *cred) |
6229 | { | 6348 | { |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index cb5f94640be6..fbdad9e1719f 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -294,7 +294,9 @@ static int nfs4_stat_to_errno(int); | |||
294 | XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \ | 294 | XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \ |
295 | 1 /* flags */ + \ | 295 | 1 /* flags */ + \ |
296 | 1 /* spa_how */ + \ | 296 | 1 /* spa_how */ + \ |
297 | 0 /* SP4_NONE (for now) */ + \ | 297 | /* max is SP4_MACH_CRED (for now) */ + \ |
298 | 1 + NFS4_OP_MAP_NUM_WORDS + \ | ||
299 | 1 + NFS4_OP_MAP_NUM_WORDS + \ | ||
298 | 1 /* implementation id array of size 1 */ + \ | 300 | 1 /* implementation id array of size 1 */ + \ |
299 | 1 /* nii_domain */ + \ | 301 | 1 /* nii_domain */ + \ |
300 | XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ | 302 | XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \ |
@@ -306,7 +308,9 @@ static int nfs4_stat_to_errno(int); | |||
306 | 1 /* eir_sequenceid */ + \ | 308 | 1 /* eir_sequenceid */ + \ |
307 | 1 /* eir_flags */ + \ | 309 | 1 /* eir_flags */ + \ |
308 | 1 /* spr_how */ + \ | 310 | 1 /* spr_how */ + \ |
309 | 0 /* SP4_NONE (for now) */ + \ | 311 | /* max is SP4_MACH_CRED (for now) */ + \ |
312 | 1 + NFS4_OP_MAP_NUM_WORDS + \ | ||
313 | 1 + NFS4_OP_MAP_NUM_WORDS + \ | ||
310 | 2 /* eir_server_owner.so_minor_id */ + \ | 314 | 2 /* eir_server_owner.so_minor_id */ + \ |
311 | /* eir_server_owner.so_major_id<> */ \ | 315 | /* eir_server_owner.so_major_id<> */ \ |
312 | XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ | 316 | XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \ |
@@ -1726,6 +1730,14 @@ static void encode_bind_conn_to_session(struct xdr_stream *xdr, | |||
1726 | *p = 0; /* use_conn_in_rdma_mode = False */ | 1730 | *p = 0; /* use_conn_in_rdma_mode = False */ |
1727 | } | 1731 | } |
1728 | 1732 | ||
1733 | static void encode_op_map(struct xdr_stream *xdr, struct nfs4_op_map *op_map) | ||
1734 | { | ||
1735 | unsigned int i; | ||
1736 | encode_uint32(xdr, NFS4_OP_MAP_NUM_WORDS); | ||
1737 | for (i = 0; i < NFS4_OP_MAP_NUM_WORDS; i++) | ||
1738 | encode_uint32(xdr, op_map->u.words[i]); | ||
1739 | } | ||
1740 | |||
1729 | static void encode_exchange_id(struct xdr_stream *xdr, | 1741 | static void encode_exchange_id(struct xdr_stream *xdr, |
1730 | struct nfs41_exchange_id_args *args, | 1742 | struct nfs41_exchange_id_args *args, |
1731 | struct compound_hdr *hdr) | 1743 | struct compound_hdr *hdr) |
@@ -1739,9 +1751,20 @@ static void encode_exchange_id(struct xdr_stream *xdr, | |||
1739 | 1751 | ||
1740 | encode_string(xdr, args->id_len, args->id); | 1752 | encode_string(xdr, args->id_len, args->id); |
1741 | 1753 | ||
1742 | p = reserve_space(xdr, 12); | 1754 | encode_uint32(xdr, args->flags); |
1743 | *p++ = cpu_to_be32(args->flags); | 1755 | encode_uint32(xdr, args->state_protect.how); |
1744 | *p++ = cpu_to_be32(0); /* zero length state_protect4_a */ | 1756 | |
1757 | switch (args->state_protect.how) { | ||
1758 | case SP4_NONE: | ||
1759 | break; | ||
1760 | case SP4_MACH_CRED: | ||
1761 | encode_op_map(xdr, &args->state_protect.enforce); | ||
1762 | encode_op_map(xdr, &args->state_protect.allow); | ||
1763 | break; | ||
1764 | default: | ||
1765 | WARN_ON_ONCE(1); | ||
1766 | break; | ||
1767 | } | ||
1745 | 1768 | ||
1746 | if (send_implementation_id && | 1769 | if (send_implementation_id && |
1747 | sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) > 1 && | 1770 | sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) > 1 && |
@@ -1752,7 +1775,7 @@ static void encode_exchange_id(struct xdr_stream *xdr, | |||
1752 | utsname()->version, utsname()->machine); | 1775 | utsname()->version, utsname()->machine); |
1753 | 1776 | ||
1754 | if (len > 0) { | 1777 | if (len > 0) { |
1755 | *p = cpu_to_be32(1); /* implementation id array length=1 */ | 1778 | encode_uint32(xdr, 1); /* implementation id array length=1 */ |
1756 | 1779 | ||
1757 | encode_string(xdr, | 1780 | encode_string(xdr, |
1758 | sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - 1, | 1781 | sizeof(CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN) - 1, |
@@ -1763,7 +1786,7 @@ static void encode_exchange_id(struct xdr_stream *xdr, | |||
1763 | p = xdr_encode_hyper(p, 0); | 1786 | p = xdr_encode_hyper(p, 0); |
1764 | *p = cpu_to_be32(0); | 1787 | *p = cpu_to_be32(0); |
1765 | } else | 1788 | } else |
1766 | *p = cpu_to_be32(0); /* implementation id array length=0 */ | 1789 | encode_uint32(xdr, 0); /* implementation id array length=0 */ |
1767 | } | 1790 | } |
1768 | 1791 | ||
1769 | static void encode_create_session(struct xdr_stream *xdr, | 1792 | static void encode_create_session(struct xdr_stream *xdr, |
@@ -5374,6 +5397,23 @@ static int decode_secinfo_no_name(struct xdr_stream *xdr, struct nfs4_secinfo_re | |||
5374 | return decode_secinfo_common(xdr, res); | 5397 | return decode_secinfo_common(xdr, res); |
5375 | } | 5398 | } |
5376 | 5399 | ||
5400 | static int decode_op_map(struct xdr_stream *xdr, struct nfs4_op_map *op_map) | ||
5401 | { | ||
5402 | __be32 *p; | ||
5403 | uint32_t bitmap_words; | ||
5404 | unsigned int i; | ||
5405 | |||
5406 | p = xdr_inline_decode(xdr, 4); | ||
5407 | bitmap_words = be32_to_cpup(p++); | ||
5408 | if (bitmap_words > NFS4_OP_MAP_NUM_WORDS) | ||
5409 | return -EIO; | ||
5410 | p = xdr_inline_decode(xdr, 4 * bitmap_words); | ||
5411 | for (i = 0; i < bitmap_words; i++) | ||
5412 | op_map->u.words[i] = be32_to_cpup(p++); | ||
5413 | |||
5414 | return 0; | ||
5415 | } | ||
5416 | |||
5377 | static int decode_exchange_id(struct xdr_stream *xdr, | 5417 | static int decode_exchange_id(struct xdr_stream *xdr, |
5378 | struct nfs41_exchange_id_res *res) | 5418 | struct nfs41_exchange_id_res *res) |
5379 | { | 5419 | { |
@@ -5397,10 +5437,22 @@ static int decode_exchange_id(struct xdr_stream *xdr, | |||
5397 | res->seqid = be32_to_cpup(p++); | 5437 | res->seqid = be32_to_cpup(p++); |
5398 | res->flags = be32_to_cpup(p++); | 5438 | res->flags = be32_to_cpup(p++); |
5399 | 5439 | ||
5400 | /* We ask for SP4_NONE */ | 5440 | res->state_protect.how = be32_to_cpup(p); |
5401 | dummy = be32_to_cpup(p); | 5441 | switch (res->state_protect.how) { |
5402 | if (dummy != SP4_NONE) | 5442 | case SP4_NONE: |
5443 | break; | ||
5444 | case SP4_MACH_CRED: | ||
5445 | status = decode_op_map(xdr, &res->state_protect.enforce); | ||
5446 | if (status) | ||
5447 | return status; | ||
5448 | status = decode_op_map(xdr, &res->state_protect.allow); | ||
5449 | if (status) | ||
5450 | return status; | ||
5451 | break; | ||
5452 | default: | ||
5453 | WARN_ON_ONCE(1); | ||
5403 | return -EIO; | 5454 | return -EIO; |
5455 | } | ||
5404 | 5456 | ||
5405 | /* server_owner4.so_minor_id */ | 5457 | /* server_owner4.so_minor_id */ |
5406 | p = xdr_inline_decode(xdr, 8); | 5458 | p = xdr_inline_decode(xdr, 8); |