diff options
-rw-r--r-- | fs/nfs/nfs4proc.c | 133 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 72 | ||||
-rw-r--r-- | include/linux/nfs_fs_sb.h | 4 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 19 |
4 files changed, 211 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); |
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index fc83d3d384d0..9f7b9192cf5e 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h | |||
@@ -90,6 +90,10 @@ struct nfs_client { | |||
90 | struct nfs41_server_owner *cl_serverowner; | 90 | struct nfs41_server_owner *cl_serverowner; |
91 | struct nfs41_server_scope *cl_serverscope; | 91 | struct nfs41_server_scope *cl_serverscope; |
92 | struct nfs41_impl_id *cl_implid; | 92 | struct nfs41_impl_id *cl_implid; |
93 | /* nfs 4.1+ state protection modes: */ | ||
94 | unsigned long cl_sp4_flags; | ||
95 | #define NFS_SP4_MACH_CRED_MINIMAL 1 /* Minimal sp4_mach_cred - state ops | ||
96 | * must use machine cred */ | ||
93 | #endif /* CONFIG_NFS_V4 */ | 97 | #endif /* CONFIG_NFS_V4 */ |
94 | 98 | ||
95 | #ifdef CONFIG_NFS_FSCACHE | 99 | #ifdef CONFIG_NFS_FSCACHE |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 7c3956d65b3e..01fd84b566f7 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -1107,6 +1107,23 @@ struct pnfs_ds_commit_info { | |||
1107 | struct pnfs_commit_bucket *buckets; | 1107 | struct pnfs_commit_bucket *buckets; |
1108 | }; | 1108 | }; |
1109 | 1109 | ||
1110 | #define NFS4_OP_MAP_NUM_LONGS \ | ||
1111 | DIV_ROUND_UP(LAST_NFS4_OP, 8 * sizeof(unsigned long)) | ||
1112 | #define NFS4_OP_MAP_NUM_WORDS \ | ||
1113 | (NFS4_OP_MAP_NUM_LONGS * sizeof(unsigned long) / sizeof(u32)) | ||
1114 | struct nfs4_op_map { | ||
1115 | union { | ||
1116 | unsigned long longs[NFS4_OP_MAP_NUM_LONGS]; | ||
1117 | u32 words[NFS4_OP_MAP_NUM_WORDS]; | ||
1118 | } u; | ||
1119 | }; | ||
1120 | |||
1121 | struct nfs41_state_protection { | ||
1122 | u32 how; | ||
1123 | struct nfs4_op_map enforce; | ||
1124 | struct nfs4_op_map allow; | ||
1125 | }; | ||
1126 | |||
1110 | #define NFS4_EXCHANGE_ID_LEN (48) | 1127 | #define NFS4_EXCHANGE_ID_LEN (48) |
1111 | struct nfs41_exchange_id_args { | 1128 | struct nfs41_exchange_id_args { |
1112 | struct nfs_client *client; | 1129 | struct nfs_client *client; |
@@ -1114,6 +1131,7 @@ struct nfs41_exchange_id_args { | |||
1114 | unsigned int id_len; | 1131 | unsigned int id_len; |
1115 | char id[NFS4_EXCHANGE_ID_LEN]; | 1132 | char id[NFS4_EXCHANGE_ID_LEN]; |
1116 | u32 flags; | 1133 | u32 flags; |
1134 | struct nfs41_state_protection state_protect; | ||
1117 | }; | 1135 | }; |
1118 | 1136 | ||
1119 | struct nfs41_server_owner { | 1137 | struct nfs41_server_owner { |
@@ -1146,6 +1164,7 @@ struct nfs41_exchange_id_res { | |||
1146 | struct nfs41_server_owner *server_owner; | 1164 | struct nfs41_server_owner *server_owner; |
1147 | struct nfs41_server_scope *server_scope; | 1165 | struct nfs41_server_scope *server_scope; |
1148 | struct nfs41_impl_id *impl_id; | 1166 | struct nfs41_impl_id *impl_id; |
1167 | struct nfs41_state_protection state_protect; | ||
1149 | }; | 1168 | }; |
1150 | 1169 | ||
1151 | struct nfs41_create_session_args { | 1170 | struct nfs41_create_session_args { |