Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd: add support for upcall version 2

Version 2 upcalls will allow the nfsd to include a hash of the kerberos
principal string in the Cld_Create upcall. If a principal is present in
the svc_cred, then the hash will be included in the Cld_Create upcall.
We attempt to use the svc_cred.cr_raw_principal (which is returned by
gssproxy) first, and then fall back to using the svc_cred.cr_principal
(which is returned by both gssproxy and rpc.svcgssd). Upon a subsequent
restart, the hash will be returned in the Cld_Gracestart downcall and
stored in the reclaim_str_hashtbl so it can be used when handling
reclaim opens.

Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Scott Mayhew and committed by
J. Bruce Fields
6ee95d1c 11a60d15

+245 -17
+209 -14
fs/nfsd/nfs4recover.c
··· 64 64 }; 65 65 66 66 static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops; 67 + static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2; 67 68 68 69 /* Globals */ 69 70 static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; ··· 178 177 const char *dname, int len, struct nfsd_net *nn) 179 178 { 180 179 struct xdr_netobj name; 180 + struct xdr_netobj princhash = { .len = 0, .data = NULL }; 181 181 struct nfs4_client_reclaim *crp; 182 182 183 183 name.data = kmemdup(dname, len, GFP_KERNEL); ··· 188 186 return; 189 187 } 190 188 name.len = len; 191 - crp = nfs4_client_to_reclaim(name, nn); 189 + crp = nfs4_client_to_reclaim(name, princhash, nn); 192 190 if (!crp) { 193 191 kfree(name.data); 194 192 return; ··· 488 486 load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) 489 487 { 490 488 struct xdr_netobj name; 489 + struct xdr_netobj princhash = { .len = 0, .data = NULL }; 491 490 492 491 if (child->d_name.len != HEXDIR_LEN - 1) { 493 492 printk("%s: illegal name %pd in recovery directory\n", ··· 503 500 goto out; 504 501 } 505 502 name.len = HEXDIR_LEN; 506 - if (!nfs4_client_to_reclaim(name, nn)) 503 + if (!nfs4_client_to_reclaim(name, princhash, nn)) 507 504 kfree(name.data); 508 505 out: 509 506 return 0; ··· 740 737 struct list_head cn_list; 741 738 unsigned int cn_xid; 742 739 bool cn_has_legacy; 740 + struct crypto_shash *cn_tfm; 743 741 }; 744 742 745 743 struct cld_upcall { ··· 750 746 union { 751 747 struct cld_msg_hdr cu_hdr; 752 748 struct cld_msg cu_msg; 749 + struct cld_msg_v2 cu_msg_v2; 753 750 } cu_u; 754 751 }; 755 752 ··· 797 792 } 798 793 799 794 static ssize_t 800 - __cld_pipe_inprogress_downcall(const struct cld_msg __user *cmsg, 795 + __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg, 801 796 struct nfsd_net *nn) 802 797 { 803 - uint8_t cmd; 804 - struct xdr_netobj name; 798 + uint8_t cmd, princhashlen; 799 + struct xdr_netobj name, princhash = { .len = 0, .data = NULL }; 805 800 uint16_t namelen; 806 801 struct cld_net *cn = nn->cld_net; 807 802 ··· 810 805 return -EFAULT; 811 806 } 812 807 if (cmd == Cld_GraceStart) { 813 - if (get_user(namelen, &cmsg->cm_u.cm_name.cn_len)) 814 - return -EFAULT; 815 - name.data = memdup_user(&cmsg->cm_u.cm_name.cn_id, namelen); 816 - if (IS_ERR_OR_NULL(name.data)) 817 - return -EFAULT; 818 - name.len = namelen; 808 + if (nn->client_tracking_ops->version >= 2) { 809 + const struct cld_clntinfo __user *ci; 810 + 811 + ci = &cmsg->cm_u.cm_clntinfo; 812 + if (get_user(namelen, &ci->cc_name.cn_len)) 813 + return -EFAULT; 814 + name.data = memdup_user(&ci->cc_name.cn_id, namelen); 815 + if (IS_ERR_OR_NULL(name.data)) 816 + return -EFAULT; 817 + name.len = namelen; 818 + get_user(princhashlen, &ci->cc_princhash.cp_len); 819 + if (princhashlen > 0) { 820 + princhash.data = memdup_user( 821 + &ci->cc_princhash.cp_data, 822 + princhashlen); 823 + if (IS_ERR_OR_NULL(princhash.data)) 824 + return -EFAULT; 825 + princhash.len = princhashlen; 826 + } else 827 + princhash.len = 0; 828 + } else { 829 + const struct cld_name __user *cnm; 830 + 831 + cnm = &cmsg->cm_u.cm_name; 832 + if (get_user(namelen, &cnm->cn_len)) 833 + return -EFAULT; 834 + name.data = memdup_user(&cnm->cn_id, namelen); 835 + if (IS_ERR_OR_NULL(name.data)) 836 + return -EFAULT; 837 + name.len = namelen; 838 + } 819 839 if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) { 820 840 name.len = name.len - 5; 821 841 memmove(name.data, name.data + 5, name.len); 822 842 cn->cn_has_legacy = true; 823 843 } 824 - if (!nfs4_client_to_reclaim(name, nn)) { 844 + if (!nfs4_client_to_reclaim(name, princhash, nn)) { 825 845 kfree(name.data); 846 + kfree(princhash.data); 826 847 return -EFAULT; 827 848 } 828 849 return nn->client_tracking_ops->msglen; ··· 861 830 { 862 831 struct cld_upcall *tmp, *cup; 863 832 struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src; 864 - struct cld_msg __user *cmsg = (struct cld_msg __user *)src; 833 + struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src; 865 834 uint32_t xid; 866 835 struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info, 867 836 nfsd_net_id); ··· 912 881 if (status == -EINPROGRESS) 913 882 return __cld_pipe_inprogress_downcall(cmsg, nn); 914 883 915 - if (copy_from_user(&cup->cu_u.cu_msg, src, mlen) != 0) 884 + if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0) 916 885 return -EFAULT; 917 886 918 887 complete(&cup->cu_done); ··· 1050 1019 1051 1020 nfsd4_cld_unregister_net(net, cn->cn_pipe); 1052 1021 rpc_destroy_pipe_data(cn->cn_pipe); 1022 + if (cn->cn_tfm) 1023 + crypto_free_shash(cn->cn_tfm); 1053 1024 kfree(nn->cld_net); 1054 1025 nn->cld_net = NULL; 1055 1026 } ··· 1134 1101 if (ret) 1135 1102 printk(KERN_ERR "NFSD: Unable to create client " 1136 1103 "record on stable storage: %d\n", ret); 1104 + } 1105 + 1106 + /* Ask daemon to create a new record */ 1107 + static void 1108 + nfsd4_cld_create_v2(struct nfs4_client *clp) 1109 + { 1110 + int ret; 1111 + struct cld_upcall *cup; 1112 + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); 1113 + struct cld_net *cn = nn->cld_net; 1114 + struct cld_msg_v2 *cmsg; 1115 + struct crypto_shash *tfm = cn->cn_tfm; 1116 + struct xdr_netobj cksum; 1117 + char *principal = NULL; 1118 + SHASH_DESC_ON_STACK(desc, tfm); 1119 + 1120 + /* Don't upcall if it's already stored */ 1121 + if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) 1122 + return; 1123 + 1124 + cup = alloc_cld_upcall(nn); 1125 + if (!cup) { 1126 + ret = -ENOMEM; 1127 + goto out_err; 1128 + } 1129 + 1130 + cmsg = &cup->cu_u.cu_msg_v2; 1131 + cmsg->cm_cmd = Cld_Create; 1132 + cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len; 1133 + memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data, 1134 + clp->cl_name.len); 1135 + if (clp->cl_cred.cr_raw_principal) 1136 + principal = clp->cl_cred.cr_raw_principal; 1137 + else if (clp->cl_cred.cr_principal) 1138 + principal = clp->cl_cred.cr_principal; 1139 + if (principal) { 1140 + desc->tfm = tfm; 1141 + cksum.len = crypto_shash_digestsize(tfm); 1142 + cksum.data = kmalloc(cksum.len, GFP_KERNEL); 1143 + if (cksum.data == NULL) { 1144 + ret = -ENOMEM; 1145 + goto out; 1146 + } 1147 + ret = crypto_shash_digest(desc, principal, strlen(principal), 1148 + cksum.data); 1149 + shash_desc_zero(desc); 1150 + if (ret) { 1151 + kfree(cksum.data); 1152 + goto out; 1153 + } 1154 + cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = cksum.len; 1155 + memcpy(cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data, 1156 + cksum.data, cksum.len); 1157 + kfree(cksum.data); 1158 + } else 1159 + cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0; 1160 + 1161 + ret = cld_pipe_upcall(cn->cn_pipe, cmsg); 1162 + if (!ret) { 1163 + ret = cmsg->cm_status; 1164 + set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); 1165 + } 1166 + 1167 + out: 1168 + free_cld_upcall(cup); 1169 + out_err: 1170 + if (ret) 1171 + pr_err("NFSD: Unable to create client record on stable storage: %d\n", 1172 + ret); 1137 1173 } 1138 1174 1139 1175 /* Ask daemon to create a new record */ ··· 1327 1225 } 1328 1226 return -ENOENT; 1329 1227 found: 1228 + crp->cr_clp = clp; 1229 + return 0; 1230 + } 1231 + 1232 + static int 1233 + nfsd4_cld_check_v2(struct nfs4_client *clp) 1234 + { 1235 + struct nfs4_client_reclaim *crp; 1236 + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); 1237 + struct cld_net *cn = nn->cld_net; 1238 + int status; 1239 + char dname[HEXDIR_LEN]; 1240 + struct xdr_netobj name; 1241 + struct crypto_shash *tfm = cn->cn_tfm; 1242 + struct xdr_netobj cksum; 1243 + char *principal = NULL; 1244 + SHASH_DESC_ON_STACK(desc, tfm); 1245 + 1246 + /* did we already find that this client is stable? */ 1247 + if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) 1248 + return 0; 1249 + 1250 + /* look for it in the reclaim hashtable otherwise */ 1251 + crp = nfsd4_find_reclaim_client(clp->cl_name, nn); 1252 + if (crp) 1253 + goto found; 1254 + 1255 + if (cn->cn_has_legacy) { 1256 + status = nfs4_make_rec_clidname(dname, &clp->cl_name); 1257 + if (status) 1258 + return -ENOENT; 1259 + 1260 + name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL); 1261 + if (!name.data) { 1262 + dprintk("%s: failed to allocate memory for name.data\n", 1263 + __func__); 1264 + return -ENOENT; 1265 + } 1266 + name.len = HEXDIR_LEN; 1267 + crp = nfsd4_find_reclaim_client(name, nn); 1268 + kfree(name.data); 1269 + if (crp) 1270 + goto found; 1271 + 1272 + } 1273 + return -ENOENT; 1274 + found: 1275 + if (crp->cr_princhash.len) { 1276 + if (clp->cl_cred.cr_raw_principal) 1277 + principal = clp->cl_cred.cr_raw_principal; 1278 + else if (clp->cl_cred.cr_principal) 1279 + principal = clp->cl_cred.cr_principal; 1280 + if (principal == NULL) 1281 + return -ENOENT; 1282 + desc->tfm = tfm; 1283 + cksum.len = crypto_shash_digestsize(tfm); 1284 + cksum.data = kmalloc(cksum.len, GFP_KERNEL); 1285 + if (cksum.data == NULL) 1286 + return -ENOENT; 1287 + status = crypto_shash_digest(desc, principal, strlen(principal), 1288 + cksum.data); 1289 + shash_desc_zero(desc); 1290 + if (status) { 1291 + kfree(cksum.data); 1292 + return -ENOENT; 1293 + } 1294 + if (memcmp(crp->cr_princhash.data, cksum.data, 1295 + crp->cr_princhash.len)) { 1296 + kfree(cksum.data); 1297 + return -ENOENT; 1298 + } 1299 + kfree(cksum.data); 1300 + } 1330 1301 crp->cr_clp = clp; 1331 1302 return 0; 1332 1303 } ··· 1555 1380 case 1: 1556 1381 nn->client_tracking_ops = &nfsd4_cld_tracking_ops; 1557 1382 break; 1383 + case 2: 1384 + nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2; 1385 + break; 1558 1386 default: 1559 1387 break; 1560 1388 } ··· 1586 1408 status = __nfsd4_init_cld_pipe(net); 1587 1409 if (status) 1588 1410 goto err_shutdown; 1411 + nn->cld_net->cn_tfm = crypto_alloc_shash("sha256", 0, 0); 1412 + if (IS_ERR(nn->cld_net->cn_tfm)) { 1413 + status = PTR_ERR(nn->cld_net->cn_tfm); 1414 + goto err_remove; 1415 + } 1589 1416 1590 1417 /* 1591 1418 * rpc pipe upcalls take 30 seconds to time out, so we don't want to ··· 1661 1478 .grace_done = nfsd4_cld_grace_done, 1662 1479 .version = 1, 1663 1480 .msglen = sizeof(struct cld_msg), 1481 + }; 1482 + 1483 + /* v2 create/check ops include the principal, if available */ 1484 + static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2 = { 1485 + .init = nfsd4_cld_tracking_init, 1486 + .exit = nfsd4_cld_tracking_exit, 1487 + .create = nfsd4_cld_create_v2, 1488 + .remove = nfsd4_cld_remove, 1489 + .check = nfsd4_cld_check_v2, 1490 + .grace_done = nfsd4_cld_grace_done, 1491 + .version = 2, 1492 + .msglen = sizeof(struct cld_msg_v2), 1664 1493 }; 1665 1494 1666 1495 /* upcall via usermodehelper */
+5 -1
fs/nfsd/nfs4state.c
··· 6887 6887 * will be freed in nfs4_remove_reclaim_record in the normal case). 6888 6888 */ 6889 6889 struct nfs4_client_reclaim * 6890 - nfs4_client_to_reclaim(struct xdr_netobj name, struct nfsd_net *nn) 6890 + nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, 6891 + struct nfsd_net *nn) 6891 6892 { 6892 6893 unsigned int strhashval; 6893 6894 struct nfs4_client_reclaim *crp; ··· 6901 6900 list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]); 6902 6901 crp->cr_name.data = name.data; 6903 6902 crp->cr_name.len = name.len; 6903 + crp->cr_princhash.data = princhash.data; 6904 + crp->cr_princhash.len = princhash.len; 6904 6905 crp->cr_clp = NULL; 6905 6906 nn->reclaim_str_hashtbl_size++; 6906 6907 } ··· 6914 6911 { 6915 6912 list_del(&crp->cr_strhash); 6916 6913 kfree(crp->cr_name.data); 6914 + kfree(crp->cr_princhash.data); 6917 6915 kfree(crp); 6918 6916 nn->reclaim_str_hashtbl_size--; 6919 6917 }
+2 -1
fs/nfsd/state.h
··· 378 378 struct list_head cr_strhash; /* hash by cr_name */ 379 379 struct nfs4_client *cr_clp; /* pointer to associated clp */ 380 380 struct xdr_netobj cr_name; /* recovery dir name */ 381 + struct xdr_netobj cr_princhash; 381 382 }; 382 383 383 384 /* A reasonable value for REPLAY_ISIZE was estimated as follows: ··· 646 645 extern void nfsd4_shutdown_copy(struct nfs4_client *clp); 647 646 extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp); 648 647 extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name, 649 - struct nfsd_net *nn); 648 + struct xdr_netobj princhash, struct nfsd_net *nn); 650 649 extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn); 651 650 652 651 struct nfs4_file *find_file(struct knfsd_fh *fh);
+29 -1
include/uapi/linux/nfsd/cld.h
··· 26 26 #include <linux/types.h> 27 27 28 28 /* latest upcall version available */ 29 - #define CLD_UPCALL_VERSION 1 29 + #define CLD_UPCALL_VERSION 2 30 30 31 31 /* defined by RFC3530 */ 32 32 #define NFS4_OPAQUE_LIMIT 1024 33 + 34 + #ifndef SHA256_DIGEST_SIZE 35 + #define SHA256_DIGEST_SIZE 32 36 + #endif 33 37 34 38 enum cld_command { 35 39 Cld_Create, /* create a record for this cm_id */ ··· 50 46 unsigned char cn_id[NFS4_OPAQUE_LIMIT]; /* client-provided */ 51 47 } __attribute__((packed)); 52 48 49 + /* sha256 hash of the kerberos principal */ 50 + struct cld_princhash { 51 + __u8 cp_len; /* length of cp_data */ 52 + unsigned char cp_data[SHA256_DIGEST_SIZE]; /* hash of principal */ 53 + } __attribute__((packed)); 54 + 55 + struct cld_clntinfo { 56 + struct cld_name cc_name; 57 + struct cld_princhash cc_princhash; 58 + } __attribute__((packed)); 59 + 53 60 /* message struct for communication with userspace */ 54 61 struct cld_msg { 55 62 __u8 cm_vers; /* upcall version */ ··· 71 56 __s64 cm_gracetime; /* grace period start time */ 72 57 struct cld_name cm_name; 73 58 __u8 cm_version; /* for getting max version */ 59 + } __attribute__((packed)) cm_u; 60 + } __attribute__((packed)); 61 + 62 + /* version 2 message can include hash of kerberos principal */ 63 + struct cld_msg_v2 { 64 + __u8 cm_vers; /* upcall version */ 65 + __u8 cm_cmd; /* upcall command */ 66 + __s16 cm_status; /* return code */ 67 + __u32 cm_xid; /* transaction id */ 68 + union { 69 + struct cld_name cm_name; 70 + __u8 cm_version; /* for getting max version */ 71 + struct cld_clntinfo cm_clntinfo; /* name & princ hash */ 74 72 } __attribute__((packed)) cm_u; 75 73 } __attribute__((packed)); 76 74