Skip to content

Commit 6ee95d1

Browse files
scottmayhewJ. Bruce Fields
authored andcommitted
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 <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]>
1 parent 11a60d1 commit 6ee95d1

File tree

4 files changed

+245
-17
lines changed

4 files changed

+245
-17
lines changed

fs/nfsd/nfs4recover.c

Lines changed: 209 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct nfsd4_client_tracking_ops {
6464
};
6565

6666
static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops;
67+
static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2;
6768

6869
/* Globals */
6970
static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
@@ -177,6 +178,7 @@ __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
177178
const char *dname, int len, struct nfsd_net *nn)
178179
{
179180
struct xdr_netobj name;
181+
struct xdr_netobj princhash = { .len = 0, .data = NULL };
180182
struct nfs4_client_reclaim *crp;
181183

182184
name.data = kmemdup(dname, len, GFP_KERNEL);
@@ -186,7 +188,7 @@ __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
186188
return;
187189
}
188190
name.len = len;
189-
crp = nfs4_client_to_reclaim(name, nn);
191+
crp = nfs4_client_to_reclaim(name, princhash, nn);
190192
if (!crp) {
191193
kfree(name.data);
192194
return;
@@ -486,6 +488,7 @@ static int
486488
load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
487489
{
488490
struct xdr_netobj name;
491+
struct xdr_netobj princhash = { .len = 0, .data = NULL };
489492

490493
if (child->d_name.len != HEXDIR_LEN - 1) {
491494
printk("%s: illegal name %pd in recovery directory\n",
@@ -500,7 +503,7 @@ load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
500503
goto out;
501504
}
502505
name.len = HEXDIR_LEN;
503-
if (!nfs4_client_to_reclaim(name, nn))
506+
if (!nfs4_client_to_reclaim(name, princhash, nn))
504507
kfree(name.data);
505508
out:
506509
return 0;
@@ -737,6 +740,7 @@ struct cld_net {
737740
struct list_head cn_list;
738741
unsigned int cn_xid;
739742
bool cn_has_legacy;
743+
struct crypto_shash *cn_tfm;
740744
};
741745

742746
struct cld_upcall {
@@ -746,6 +750,7 @@ struct cld_upcall {
746750
union {
747751
struct cld_msg_hdr cu_hdr;
748752
struct cld_msg cu_msg;
753+
struct cld_msg_v2 cu_msg_v2;
749754
} cu_u;
750755
};
751756

@@ -792,11 +797,11 @@ cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg)
792797
}
793798

794799
static ssize_t
795-
__cld_pipe_inprogress_downcall(const struct cld_msg __user *cmsg,
800+
__cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
796801
struct nfsd_net *nn)
797802
{
798-
uint8_t cmd;
799-
struct xdr_netobj name;
803+
uint8_t cmd, princhashlen;
804+
struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
800805
uint16_t namelen;
801806
struct cld_net *cn = nn->cld_net;
802807

@@ -805,19 +810,45 @@ __cld_pipe_inprogress_downcall(const struct cld_msg __user *cmsg,
805810
return -EFAULT;
806811
}
807812
if (cmd == Cld_GraceStart) {
808-
if (get_user(namelen, &cmsg->cm_u.cm_name.cn_len))
809-
return -EFAULT;
810-
name.data = memdup_user(&cmsg->cm_u.cm_name.cn_id, namelen);
811-
if (IS_ERR_OR_NULL(name.data))
812-
return -EFAULT;
813-
name.len = namelen;
813+
if (nn->client_tracking_ops->version >= 2) {
814+
const struct cld_clntinfo __user *ci;
815+
816+
ci = &cmsg->cm_u.cm_clntinfo;
817+
if (get_user(namelen, &ci->cc_name.cn_len))
818+
return -EFAULT;
819+
name.data = memdup_user(&ci->cc_name.cn_id, namelen);
820+
if (IS_ERR_OR_NULL(name.data))
821+
return -EFAULT;
822+
name.len = namelen;
823+
get_user(princhashlen, &ci->cc_princhash.cp_len);
824+
if (princhashlen > 0) {
825+
princhash.data = memdup_user(
826+
&ci->cc_princhash.cp_data,
827+
princhashlen);
828+
if (IS_ERR_OR_NULL(princhash.data))
829+
return -EFAULT;
830+
princhash.len = princhashlen;
831+
} else
832+
princhash.len = 0;
833+
} else {
834+
const struct cld_name __user *cnm;
835+
836+
cnm = &cmsg->cm_u.cm_name;
837+
if (get_user(namelen, &cnm->cn_len))
838+
return -EFAULT;
839+
name.data = memdup_user(&cnm->cn_id, namelen);
840+
if (IS_ERR_OR_NULL(name.data))
841+
return -EFAULT;
842+
name.len = namelen;
843+
}
814844
if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
815845
name.len = name.len - 5;
816846
memmove(name.data, name.data + 5, name.len);
817847
cn->cn_has_legacy = true;
818848
}
819-
if (!nfs4_client_to_reclaim(name, nn)) {
849+
if (!nfs4_client_to_reclaim(name, princhash, nn)) {
820850
kfree(name.data);
851+
kfree(princhash.data);
821852
return -EFAULT;
822853
}
823854
return nn->client_tracking_ops->msglen;
@@ -830,7 +861,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
830861
{
831862
struct cld_upcall *tmp, *cup;
832863
struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src;
833-
struct cld_msg __user *cmsg = (struct cld_msg __user *)src;
864+
struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src;
834865
uint32_t xid;
835866
struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
836867
nfsd_net_id);
@@ -881,7 +912,7 @@ cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
881912
if (status == -EINPROGRESS)
882913
return __cld_pipe_inprogress_downcall(cmsg, nn);
883914

884-
if (copy_from_user(&cup->cu_u.cu_msg, src, mlen) != 0)
915+
if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0)
885916
return -EFAULT;
886917

887918
complete(&cup->cu_done);
@@ -1019,6 +1050,8 @@ nfsd4_remove_cld_pipe(struct net *net)
10191050

10201051
nfsd4_cld_unregister_net(net, cn->cn_pipe);
10211052
rpc_destroy_pipe_data(cn->cn_pipe);
1053+
if (cn->cn_tfm)
1054+
crypto_free_shash(cn->cn_tfm);
10221055
kfree(nn->cld_net);
10231056
nn->cld_net = NULL;
10241057
}
@@ -1103,6 +1136,75 @@ nfsd4_cld_create(struct nfs4_client *clp)
11031136
"record on stable storage: %d\n", ret);
11041137
}
11051138

1139+
/* Ask daemon to create a new record */
1140+
static void
1141+
nfsd4_cld_create_v2(struct nfs4_client *clp)
1142+
{
1143+
int ret;
1144+
struct cld_upcall *cup;
1145+
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1146+
struct cld_net *cn = nn->cld_net;
1147+
struct cld_msg_v2 *cmsg;
1148+
struct crypto_shash *tfm = cn->cn_tfm;
1149+
struct xdr_netobj cksum;
1150+
char *principal = NULL;
1151+
SHASH_DESC_ON_STACK(desc, tfm);
1152+
1153+
/* Don't upcall if it's already stored */
1154+
if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1155+
return;
1156+
1157+
cup = alloc_cld_upcall(nn);
1158+
if (!cup) {
1159+
ret = -ENOMEM;
1160+
goto out_err;
1161+
}
1162+
1163+
cmsg = &cup->cu_u.cu_msg_v2;
1164+
cmsg->cm_cmd = Cld_Create;
1165+
cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len;
1166+
memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data,
1167+
clp->cl_name.len);
1168+
if (clp->cl_cred.cr_raw_principal)
1169+
principal = clp->cl_cred.cr_raw_principal;
1170+
else if (clp->cl_cred.cr_principal)
1171+
principal = clp->cl_cred.cr_principal;
1172+
if (principal) {
1173+
desc->tfm = tfm;
1174+
cksum.len = crypto_shash_digestsize(tfm);
1175+
cksum.data = kmalloc(cksum.len, GFP_KERNEL);
1176+
if (cksum.data == NULL) {
1177+
ret = -ENOMEM;
1178+
goto out;
1179+
}
1180+
ret = crypto_shash_digest(desc, principal, strlen(principal),
1181+
cksum.data);
1182+
shash_desc_zero(desc);
1183+
if (ret) {
1184+
kfree(cksum.data);
1185+
goto out;
1186+
}
1187+
cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = cksum.len;
1188+
memcpy(cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
1189+
cksum.data, cksum.len);
1190+
kfree(cksum.data);
1191+
} else
1192+
cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0;
1193+
1194+
ret = cld_pipe_upcall(cn->cn_pipe, cmsg);
1195+
if (!ret) {
1196+
ret = cmsg->cm_status;
1197+
set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1198+
}
1199+
1200+
out:
1201+
free_cld_upcall(cup);
1202+
out_err:
1203+
if (ret)
1204+
pr_err("NFSD: Unable to create client record on stable storage: %d\n",
1205+
ret);
1206+
}
1207+
11061208
/* Ask daemon to create a new record */
11071209
static void
11081210
nfsd4_cld_remove(struct nfs4_client *clp)
@@ -1229,6 +1331,79 @@ nfsd4_cld_check(struct nfs4_client *clp)
12291331
return 0;
12301332
}
12311333

1334+
static int
1335+
nfsd4_cld_check_v2(struct nfs4_client *clp)
1336+
{
1337+
struct nfs4_client_reclaim *crp;
1338+
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1339+
struct cld_net *cn = nn->cld_net;
1340+
int status;
1341+
char dname[HEXDIR_LEN];
1342+
struct xdr_netobj name;
1343+
struct crypto_shash *tfm = cn->cn_tfm;
1344+
struct xdr_netobj cksum;
1345+
char *principal = NULL;
1346+
SHASH_DESC_ON_STACK(desc, tfm);
1347+
1348+
/* did we already find that this client is stable? */
1349+
if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1350+
return 0;
1351+
1352+
/* look for it in the reclaim hashtable otherwise */
1353+
crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
1354+
if (crp)
1355+
goto found;
1356+
1357+
if (cn->cn_has_legacy) {
1358+
status = nfs4_make_rec_clidname(dname, &clp->cl_name);
1359+
if (status)
1360+
return -ENOENT;
1361+
1362+
name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
1363+
if (!name.data) {
1364+
dprintk("%s: failed to allocate memory for name.data\n",
1365+
__func__);
1366+
return -ENOENT;
1367+
}
1368+
name.len = HEXDIR_LEN;
1369+
crp = nfsd4_find_reclaim_client(name, nn);
1370+
kfree(name.data);
1371+
if (crp)
1372+
goto found;
1373+
1374+
}
1375+
return -ENOENT;
1376+
found:
1377+
if (crp->cr_princhash.len) {
1378+
if (clp->cl_cred.cr_raw_principal)
1379+
principal = clp->cl_cred.cr_raw_principal;
1380+
else if (clp->cl_cred.cr_principal)
1381+
principal = clp->cl_cred.cr_principal;
1382+
if (principal == NULL)
1383+
return -ENOENT;
1384+
desc->tfm = tfm;
1385+
cksum.len = crypto_shash_digestsize(tfm);
1386+
cksum.data = kmalloc(cksum.len, GFP_KERNEL);
1387+
if (cksum.data == NULL)
1388+
return -ENOENT;
1389+
status = crypto_shash_digest(desc, principal, strlen(principal),
1390+
cksum.data);
1391+
shash_desc_zero(desc);
1392+
if (status) {
1393+
kfree(cksum.data);
1394+
return -ENOENT;
1395+
}
1396+
if (memcmp(crp->cr_princhash.data, cksum.data,
1397+
crp->cr_princhash.len)) {
1398+
kfree(cksum.data);
1399+
return -ENOENT;
1400+
}
1401+
kfree(cksum.data);
1402+
}
1403+
crp->cr_clp = clp;
1404+
return 0;
1405+
}
1406+
12321407
static int
12331408
nfsd4_cld_grace_start(struct nfsd_net *nn)
12341409
{
@@ -1380,6 +1555,9 @@ nfsd4_cld_get_version(struct nfsd_net *nn)
13801555
case 1:
13811556
nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
13821557
break;
1558+
case 2:
1559+
nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2;
1560+
break;
13831561
default:
13841562
break;
13851563
}
@@ -1408,6 +1586,11 @@ nfsd4_cld_tracking_init(struct net *net)
14081586
status = __nfsd4_init_cld_pipe(net);
14091587
if (status)
14101588
goto err_shutdown;
1589+
nn->cld_net->cn_tfm = crypto_alloc_shash("sha256", 0, 0);
1590+
if (IS_ERR(nn->cld_net->cn_tfm)) {
1591+
status = PTR_ERR(nn->cld_net->cn_tfm);
1592+
goto err_remove;
1593+
}
14111594

14121595
/*
14131596
* rpc pipe upcalls take 30 seconds to time out, so we don't want to
@@ -1480,6 +1663,18 @@ static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
14801663
.msglen = sizeof(struct cld_msg),
14811664
};
14821665

1666+
/* v2 create/check ops include the principal, if available */
1667+
static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2 = {
1668+
.init = nfsd4_cld_tracking_init,
1669+
.exit = nfsd4_cld_tracking_exit,
1670+
.create = nfsd4_cld_create_v2,
1671+
.remove = nfsd4_cld_remove,
1672+
.check = nfsd4_cld_check_v2,
1673+
.grace_done = nfsd4_cld_grace_done,
1674+
.version = 2,
1675+
.msglen = sizeof(struct cld_msg_v2),
1676+
};
1677+
14831678
/* upcall via usermodehelper */
14841679
static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack";
14851680
module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog),

fs/nfsd/nfs4state.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6887,7 +6887,8 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn)
68876887
* will be freed in nfs4_remove_reclaim_record in the normal case).
68886888
*/
68896889
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)
68916892
{
68926893
unsigned int strhashval;
68936894
struct nfs4_client_reclaim *crp;
@@ -6900,6 +6901,8 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct nfsd_net *nn)
69006901
list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]);
69016902
crp->cr_name.data = name.data;
69026903
crp->cr_name.len = name.len;
6904+
crp->cr_princhash.data = princhash.data;
6905+
crp->cr_princhash.len = princhash.len;
69036906
crp->cr_clp = NULL;
69046907
nn->reclaim_str_hashtbl_size++;
69056908
}
@@ -6911,6 +6914,7 @@ nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn)
69116914
{
69126915
list_del(&crp->cr_strhash);
69136916
kfree(crp->cr_name.data);
6917+
kfree(crp->cr_princhash.data);
69146918
kfree(crp);
69156919
nn->reclaim_str_hashtbl_size--;
69166920
}

fs/nfsd/state.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ struct nfs4_client_reclaim {
378378
struct list_head cr_strhash; /* hash by cr_name */
379379
struct nfs4_client *cr_clp; /* pointer to associated clp */
380380
struct xdr_netobj cr_name; /* recovery dir name */
381+
struct xdr_netobj cr_princhash;
381382
};
382383

383384
/* A reasonable value for REPLAY_ISIZE was estimated as follows:
@@ -645,7 +646,7 @@ extern void nfsd4_shutdown_callback(struct nfs4_client *);
645646
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
646647
extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
647648
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
648-
struct nfsd_net *nn);
649+
struct xdr_netobj princhash, struct nfsd_net *nn);
649650
extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
650651

651652
struct nfs4_file *find_file(struct knfsd_fh *fh);

0 commit comments

Comments
 (0)