[PATCH 13/13] NFSD: Label change notification for NFSv4 Server

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



NFSv4 Provides methods for detecting changes in attributes and recalling
delegations when state has changed on the server. This has been extended to
include this functionality for label changes on the server. This patch provides
the server implementation for a new callback used to signal relabeling of
files.

Signed-off-by: David P. Quigley <dpquigl@xxxxxxxxxxxxx>
Signed-off-by: Matthew N. Dodd <Matthew.Dodd@xxxxxxxxxx>
---
 fs/nfsd/nfs4callback.c     |  125 +++++++++++++++++++++++++++++++++++++++++++-
 fs/nfsd/nfs4state.c        |   95 ++++++++++++++++++++++++++++++++-
 include/linux/nfsd/state.h |   17 ++++++
 3 files changed, 233 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 9d536a8..6fe48b1 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -40,6 +40,8 @@
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/kthread.h>
+#include <linux/security.h>
+#include <linux/xattr.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/clnt.h>
@@ -49,6 +51,7 @@
 #include <linux/nfs4.h>
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
+#define NFS_MAXLABELLEN			255
 
 #define NFSPROC4_CB_NULL 0
 #define NFSPROC4_CB_COMPOUND 1
@@ -61,10 +64,12 @@ static const struct rpc_call_ops nfs4_cb_null_ops;
 enum {
         NFSPROC4_CLNT_CB_NULL = 0,
 	NFSPROC4_CLNT_CB_RECALL,
+	NFSPROC4_CLNT_CB_RELABEL,
 };
 
 enum nfs_cb_opnum4 {
 	OP_CB_RECALL            = 4,
+	OP_CB_RELABEL		= 5,
 };
 
 #define NFS4_MAXTAGLEN		20
@@ -76,6 +81,8 @@ enum nfs_cb_opnum4 {
 #define op_enc_sz			1
 #define op_dec_sz			2
 #define enc_nfs4_fh_sz			(1 + (NFS4_FHSIZE >> 2))
+#define	enc_nfs4_label_sz		(1 + (NFS_MAXLABELLEN >> 2))
+#define	enc_i_ino_sz			1
 #define enc_stateid_sz			(NFS4_STATEID_SIZE >> 2)
 #define NFS4_enc_cb_recall_sz		(cb_compound_enc_hdr_sz +       \
 					1 + enc_stateid_sz +            \
@@ -84,6 +91,13 @@ enum nfs_cb_opnum4 {
 #define NFS4_dec_cb_recall_sz		(cb_compound_dec_hdr_sz  +      \
 					op_dec_sz)
 
+#define	NFS4_enc_cb_relabel_sz		(cb_compound_enc_hdr_sz +	\
+					 op_enc_sz +			\
+					 enc_i_ino_sz +			\
+					 enc_nfs4_fh_sz +		\
+					 enc_nfs4_label_sz)
+#define	NFS4_dec_cb_relabel_sz		(cb_compound_dec_hdr_sz  +      \
+					 op_dec_sz)
 /*
 * Generic encode routines from fs/nfs/nfs4xdr.c
 */
@@ -233,6 +247,24 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
 }
 
 static int
+encode_cb_relabel(struct xdr_stream *xdr, struct nfs4_cb_relabel *cbr)
+{
+	__be32 *p;
+
+	RESERVE_SPACE(12 + 8 + cbr->fhlen + cbr->label_len);
+	WRITE32(OP_CB_RELABEL);
+	/* ino_t */
+	p = xdr_encode_hyper(p, cbr->ino);
+	/* File handle */
+	WRITE32(cbr->fhlen);
+	WRITEMEM(cbr->fhval, cbr->fhlen);
+	/* Label */
+	WRITE32(cbr->label_len);
+	WRITEMEM(cbr->label, cbr->label_len);
+	return 0;
+}
+
+static int
 nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
 {
 	struct xdr_stream xdrs, *xdr = &xdrs;
@@ -256,6 +288,19 @@ nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_cb_recall *a
 	return (encode_cb_recall(&xdr, args));
 }
 
+static int
+nfs4_xdr_enc_cb_relabel(struct rpc_rqst *req, __be32 *p, struct nfs4_cb_relabel *args)
+{
+	struct xdr_stream xdr;
+	struct nfs4_cb_compound_hdr hdr = {
+		.ident = args->ident,
+		.nops   = 1,
+	};
+
+	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+	encode_cb_compound_hdr(&xdr, &hdr);
+	return (encode_cb_relabel(&xdr, args));
+}
 
 static int
 decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
@@ -314,6 +359,22 @@ out:
 	return status;
 }
 
+static int
+nfs4_xdr_dec_cb_relabel(struct rpc_rqst *rqstp, __be32 *p)
+{
+	struct xdr_stream xdr;
+	struct nfs4_cb_compound_hdr hdr;
+	int status;
+
+	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+	status = decode_cb_compound_hdr(&xdr, &hdr);
+	if (status)
+		goto out;
+	status = decode_cb_op_hdr(&xdr, OP_CB_RELABEL);
+out:
+	return status;
+}
+
 /*
  * RPC procedure tables
  */
@@ -329,8 +390,9 @@ out:
 }
 
 static struct rpc_procinfo     nfs4_cb_procedures[] = {
-    PROC(CB_NULL,      NULL,     enc_cb_null,     dec_cb_null),
-    PROC(CB_RECALL,    COMPOUND,   enc_cb_recall,      dec_cb_recall),
+	PROC(CB_NULL,		NULL,		enc_cb_null,		dec_cb_null),
+	PROC(CB_RECALL,		COMPOUND,	enc_cb_recall,		dec_cb_recall),
+	PROC(CB_RELABEL,	COMPOUND,	enc_cb_relabel,		dec_cb_relabel),
 };
 
 static struct rpc_version       nfs_cb_version4 = {
@@ -490,3 +552,62 @@ out_put_cred:
 	nfs4_put_delegation(dp);
 	return;
 }
+
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+void
+nfsd4_cb_relabel(struct nfs4_stateid *stp)
+{
+	struct nfs4_client *clp = stp->st_stateowner->so_client;
+	struct rpc_clnt *clnt = clp->cl_callback.cb_client;
+	struct nfs4_cb_relabel args;
+	struct rpc_message msg = {
+		.rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RELABEL],
+		.rpc_argp       = &args,
+	};
+	void *context;
+	const char *key;
+	int len;
+	int retries = 1;
+	int status = 0;
+
+	key = security_inode_xattr_getname() + XATTR_SECURITY_PREFIX_LEN;
+	len = security_inode_getsecurity(stp->st_file->fi_inode, key, &context
+						, true);
+	if (len < 0) {
+		goto out;
+	}
+	if (len > NFS_MAXLABELLEN) {
+		security_release_secctx(context, len);
+		goto out;
+	}
+	args.ident = clp->cl_callback.cb_ident;
+	args.ino = stp->st_file->fi_inode->i_ino;
+	args.fhlen = stp->st_fh.fh_handle.fh_size;
+	memcpy(args.fhval, &stp->st_fh.fh_handle.fh_base, args.fhlen);
+	args.label = context;
+	args.label_len = len;
+
+	status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
+	while (retries--) {
+		switch (status) {
+			case -EIO:
+				/* Network partition? */
+			case -EBADHANDLE:
+			case -NFS4ERR_BAD_STATEID:
+				/* Race: client probably got cb_recall
+				 * before open reply granting delegation */
+				break;
+			default:
+				goto out_put_cred;
+		}
+		ssleep(2);
+		status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
+	}
+
+out_put_cred:
+	security_release_secctx(context, len);
+out:
+	/* XXX: need to release references! */
+	return;
+}
+#endif
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 31673cd..dcd7f69 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -79,6 +79,17 @@ static void release_stateid_lockowners(struct nfs4_stateid *open_stp);
 static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
 static void nfs4_set_recdir(char *recdir);
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+static struct inotify_handle *nfsd_ih = NULL;
+static void nfsd_watch_event(struct inotify_watch *, u32, u32, u32,
+		const char *, struct inode *);
+static void nfsd_watch_destroy(struct inotify_watch *);
+static struct inotify_operations nfsd_state_event_handler = {
+	.handle_event	= nfsd_watch_event,
+	.destroy_watch	= nfsd_watch_destroy,
+};
+#endif
+
 /* Locking:
  *
  * client_mutex:
@@ -135,7 +146,13 @@ free_nfs4_file(struct kref *kref)
 	struct nfs4_file *fp = container_of(kref, struct nfs4_file, fi_ref);
 	list_del(&fp->fi_hash);
 	iput(fp->fi_inode);
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+	(void)inotify_rm_watch(nfsd_ih, &fp->fi_wdata);
+	put_inotify_watch(&fp->fi_wdata);
+	/* Defer free to nfsd_watch_destroy() */
+#else
 	kmem_cache_free(file_slab, fp);
+#endif
 }
 
 static inline void
@@ -988,6 +1005,11 @@ alloc_init_file(struct inode *ino)
 		fp->fi_inode = igrab(ino);
 		fp->fi_id = current_fileid++;
 		fp->fi_had_conflict = false;
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+		inotify_init_watch(&fp->fi_wdata);
+		get_inotify_watch(&fp->fi_wdata);
+		(void)inotify_add_watch(nfsd_ih, &fp->fi_wdata, ino, IN_LABEL);
+#endif
 		return fp;
 	}
 	return NULL;
@@ -1137,7 +1159,9 @@ release_stateowner(struct nfs4_stateowner *sop)
 }
 
 static inline void
-init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) {
+init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct svc_fh *fh,
+			struct nfsd4_open *open)
+{
 	struct nfs4_stateowner *sop = open->op_stateowner;
 	unsigned int hashval = stateid_hashval(sop->so_id, fp->fi_id);
 
@@ -1160,6 +1184,9 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *
 	__set_bit(open->op_share_access, &stp->st_access_bmap);
 	__set_bit(open->op_share_deny, &stp->st_deny_bmap);
 	stp->st_openstp = NULL;
+
+	fh_init(&stp->st_fh, NFS4_FHSIZE);
+	(void)fh_compose(&stp->st_fh, fh->fh_export, fh->fh_dentry, fh);
 }
 
 static void
@@ -1177,6 +1204,7 @@ release_stateid(struct nfs4_stateid *stp, int flags)
 	} else if (flags & LOCK_STATE)
 		locks_remove_posix(filp, (fl_owner_t) stp->st_stateowner);
 	put_nfs4_file(stp->st_file);
+	fh_put(&stp->st_fh);
 	kmem_cache_free(stateid_slab, stp);
 }
 
@@ -1791,7 +1819,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
 		status = nfs4_new_open(rqstp, &stp, dp, current_fh, flags);
 		if (status)
 			goto out;
-		init_stateid(stp, fp, open);
+		init_stateid(stp, fp, current_fh, open);
 		status = nfsd4_truncate(rqstp, current_fh, open);
 		if (status) {
 			release_stateid(stp, OPEN_STATE);
@@ -3241,6 +3269,9 @@ nfs4_state_start(void)
 {
 	if (nfs4_init)
 		return;
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+	nfsd_ih = inotify_init(&nfsd_state_event_handler);
+#endif
 	nfsd4_load_reboot_recovery_data();
 	__nfs4_state_start();
 	nfs4_init = 1;
@@ -3350,3 +3381,63 @@ nfs4_reset_lease(time_t leasetime)
 	user_lease_time = leasetime;
 	unlock_kernel();
 }
+
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+
+	static int
+do_relabel(void *__stp)
+{
+	struct nfs4_stateid *stp = __stp;
+
+	daemonize("nfsv4-relabel");
+
+	nfsd4_cb_relabel(stp);
+	return (0);
+}
+
+	static void
+nfsd_watch_event(struct inotify_watch *watch, u32 wd, u32 mask, u32 cookie,
+		const char *name, struct inode *inode)
+{
+	struct nfs4_file *fp;
+	struct nfs4_stateid *stp;
+	struct task_struct *t;
+
+	fp = container_of(watch, struct nfs4_file, fi_wdata);
+
+	if (mask & IN_IGNORED) {
+		put_inotify_watch(&fp->fi_wdata);
+		return;
+	}
+
+	if ((mask & IN_LABEL) == 0)
+		return;
+
+	list_for_each_entry(stp, &fp->fi_stateids, st_perfile) {
+		struct nfs4_client *clp = stp->st_stateowner->so_client;
+
+		/* XXX: need to refcount something! */
+
+		printk("%s() STP client \"%.*s\" fh \"%s\"\n", __func__,
+				clp->cl_name.len, clp->cl_name.data,
+				SVCFH_fmt(&stp->st_fh));
+
+		t = kthread_run(do_relabel, stp, "%s", "nfs4_cb_relabel");
+		if (IS_ERR(t)) {
+			printk(KERN_INFO "NFSD: Callback thread failed for "
+					"for client (clientid %08x/%08x)\n",
+					clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
+		}
+
+	}
+}
+
+	static void
+nfsd_watch_destroy(struct inotify_watch *watch)
+{
+	struct nfs4_file *fp;
+
+	fp = container_of(watch, struct nfs4_file, fi_wdata);
+	kmem_cache_free(file_slab, fp);
+}
+#endif	/* CONFIG_NFSD_V4_SECURITY_LABEL */
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index db348f7..043f800 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -40,6 +40,7 @@
 #include <linux/list.h>
 #include <linux/kref.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/inotify.h>
 
 #define NFS4_OPAQUE_LIMIT 1024
 typedef struct {
@@ -71,6 +72,15 @@ struct nfs4_cb_recall {
 	struct nfs4_delegation	*cbr_dp;
 };
 
+struct nfs4_cb_relabel {
+	u32			ident;
+	unsigned long		ino;
+	u32			fhlen;
+	u32			fhval[NFS4_FHSIZE];
+	char *			label;
+	int			label_len;
+};
+
 struct nfs4_delegation {
 	struct list_head	dl_perfile;
 	struct list_head	dl_perclnt;
@@ -85,6 +95,7 @@ struct nfs4_delegation {
 	struct nfs4_cb_recall	dl_recall;
 };
 
+#define dl_ident	dl_recall.cbr_ident
 #define dl_stateid      dl_recall.cbr_stateid
 #define dl_fhlen        dl_recall.cbr_fhlen
 #define dl_fhval        dl_recall.cbr_fhval
@@ -225,6 +236,10 @@ struct nfs4_file {
 	u32                     fi_id;      /* used with stateowner->so_id 
 					     * for stateid_hashtbl hash */
 	bool			fi_had_conflict;
+
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+	struct inotify_watch	fi_wdata;
+#endif
 };
 
 /*
@@ -251,6 +266,7 @@ struct nfs4_stateid {
 	struct list_head              st_lockowners;
 	struct nfs4_stateowner      * st_stateowner;
 	struct nfs4_file            * st_file;
+	struct svc_fh                 st_fh;
 	stateid_t                     st_stateid;
 	struct file                 * st_vfs_file;
 	unsigned long                 st_access_bmap;
@@ -284,6 +300,7 @@ extern void put_nfs4_client(struct nfs4_client *clp);
 extern void nfs4_free_stateowner(struct kref *kref);
 extern void nfsd4_probe_callback(struct nfs4_client *clp);
 extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
+extern void nfsd4_cb_relabel(struct nfs4_stateid *stp);
 extern void nfs4_put_delegation(struct nfs4_delegation *dp);
 extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
 extern void nfsd4_init_recdir(char *recdir_name);
-- 
1.5.3.4



--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux