Skip to contentSkip to conversation

MITM tool for NFS traffic on linux

PATCH In review Benjamin Coddington 3 participants 9 messages May 27, 2015 – Jun 25, 2015

Subsystems (2)
lore
Share & tools
lore
#1 lore raw

PATCH 1/3 Fix default arg order error on swig > 1.x

In reply to Benjamin Coddington

The gssapi.py module ends up with
def initSecContext(name, context=None, INPUT=0, cred=0, mech, flags=2, time=0, chan=0):

which creates the error "SyntaxError: non-default argument follows default argument" on import. Revert to earlier swig behavior of using compact default args.

---
 gssapi/gssapi.i |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
gssapi/gssapi.i +2 -0
index 5b66ce0..28656c7 100644
@@ -487,6 +487,7 @@ OM_uint32 reordered_init_sec_context
 %apply gss_buffer_t OUTPUT {gss_buffer_t token};
 %apply OM_uint32 *OUTPUT {OM_uint32 *flags, OM_uint32 *time};
 %feature("autodoc", "initSecContext(gss_name_t name, gss_ctx_id_t *context=None, string token=None, gss_cred_id_t cred=None, gss_OID mech=krb5oid, int flags=0, int time=0, gss_channel_bindings_t chan=None) -> context, mech, token, flags, time");
+%feature("compactdefaultargs");
 OM_uint32 reordered_init_sec_context
 	        (OM_uint32 *minor,		/* minor_status */
 		 gss_name_t name,			/* target_name IN*/
@@ -507,6 +508,7 @@ OM_uint32 reordered_init_sec_context
 %clear gss_OID *mech;
 %clear gss_buffer_t token;
 %clear OM_uint32 *flags, OM_uint32 *time;
+%feature("compactdefaultargs", 0);
 
 /********/
 
Signature · 1 line
1.7.1
#3 lore raw

PATCH 2/3 Add a tool for modification of NFS network traffic: itm

In reply to Benjamin Coddington

Provide a framework to allow the inspection and modification of NFS network traffic between an existing server and client, provided one of them is able to use netfilter's libnfqueue. This essentially linux-only tool is similar to nfs-proxy in that it can be used to inject protocol errors and other behaviors, however it is more convenient to use on linux as it can quickly be inserted or removed from existing server-client pairs.

---
 itm/README               |   26 +++++
 itm/handlers.py          |    9 ++
 itm/handlers/default.py  |   19 ++++
 itm/handlers/example.py  |   14 +++
 itm/itm.py               |  230 ++++++++++++++++++++++++++++++++++++++++++++++
 itm/run_itm.sh           |   41 ++++++++
 itm/use_local.py         |   14 +++
 7 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 itm/README
 create mode 100644 itm/__init__.py
 create mode 100644 itm/handlers.py
 create mode 100644 itm/handlers/__init__.py
 create mode 100644 itm/handlers/default.py
 create mode 100644 itm/handlers/example.py
 create mode 100755 itm/itm.py
 create mode 100755 itm/run_itm.sh
 create mode 100644 itm/use_local.py
itm/README new file +26 -0
new file mode 100644
index 0000000..5bd435e
@@ -0,0 +1,26 @@
+PyNFS ITM - In-the-Middle
+
+This tool uses bits of the main pynfs encoding/decoding functionality to
+allow the inspection and modification of NFS network traffic between clients
+and servers.  More than a few bugs have been difficult to reproduce from
+above the filesystem, and some bugs require access to specialized servers
+that behave in strange ways.  This tool may allow a shorter path to problem
+reproduction when the on-wire behavior is known be reproduced by  enabling
+the quick injection and then removal of behaviors written into handlers.
+
+This tool relies on libnetfilter_queue to forward packets from a Linux
+client or server's netfilter to userspace to accept or modify the payload.
+
+Requirements:
+ nfqueue-bindings
+	https://www.wzdftpd.net/redmine/projects/nfqueue-bindings/wiki
+ python-dpkt
+
+Usage:
+First, edit itm/run_itm.sh to configure either the client or server's
+hostname.  If running on a client, set the server's name; if on a server,
+set the client's hostname.
+
+./itm/run_itm.sh [name of handler] 
+
+Handlers can be found/created in itm/handlers/
itm/__init__.py new file +0 -0
new file mode 100644
index 0000000..e69de29
itm/handlers.py new file +9 -0
new file mode 100644
index 0000000..8fba14a
@@ -0,0 +1,9 @@
+class BaseHandler(object):
+	def __init__(self):
+		pass
+
+	def will_handle(self, cb_info):
+		return 0;
+
+	def handle(self, cb_info):
+		return 0;
itm/handlers/__init__.py new file +0 -0
new file mode 100644
index 0000000..e69de29
itm/handlers/default.py new file +19 -0
new file mode 100644
index 0000000..89d828a
@@ -0,0 +1,19 @@
+import itm
+from itm.handlers import BaseHandler
+
+class Handler(BaseHandler):
+	''' this default handler only prints out information about
+		rpc message '''
+	def __init__(self):
+		print "init default itm.handler"
+
+	def will_handle(self, cb_info):
+		# turn me on!
+		return 0
+
+	def handle(self, cb_info):
+		cb_info.dump_IP()
+		#cb_info.dump_RPC()
+		#cb_info.dump_NFS()
+		#cb_info.dump_data()
+		return 0
itm/handlers/example.py new file +14 -0
new file mode 100644
index 0000000..7395493
@@ -0,0 +1,14 @@
+from itm.handlers import BaseHandler
+
+class Handler(BaseHandler):
+	def will_handle(self, cb_info):
+		# implement stateful setup or conditions here
+		# return 1 if you want to handle this record
+		return 0
+
+	def handle(self, cb_info):
+		# modify the payload here
+		# return 1 to submit the modified payload
+		return 0
+		
+
itm/itm.py new file +230 -0

230 lines not on this page · raw message

itm/run_itm.sh new file +41 -0

41 lines not on this page · raw message

itm/use_local.py new file +14 -0
new file mode 100644
index 0000000..bbbb472
@@ -0,0 +1,14 @@
+import sys
+import os
+from os.path import join, split
+cwd = os.path.dirname(os.path.realpath(__file__))
+if True or cwd not in sys.path:
+    head, tail = split(cwd)
+    dirs = [ join(head, "gssapi"),
+             join(head, "xdr"),
+             join(head, "ply"),
+             join(head, "rpc"),
+             join(head, "nfs4.1"),
+             cwd,
+             ]
+    sys.path[1:1] = dirs
Signature · 1 line
1.7.1
#0 lore raw

cover MITM tool for NFS traffic on linux

What follows is a small tool I think may be convenient to test and reproduce certain types of bugs that are difficult to create from above the filesystem, but are clearly problematic and have well-defined network triggers. Anna's recent BAD_STATEID on WRITES with delegation is a good example of that. This tool uses netfilters NFQUEUE target to allow a linux host to modify the NFS network traffic between existing clients and servers. In that sense, it is very similar to nfs-proxy, however I find it to be much more convenient to use, as it can be quickly inserted and removed from an existing network conection.

Benjamin Coddington (3):
Fix default arg order error on swig > 1.x
Add a tool for modification of NFS network traffic: itm
itm: add a handler that truncates READDIR response page data

 gssapi/gssapi.i                       |    2 +
 itm/README                            |   26 ++++
 itm/handlers.py                       |    9 ++
 itm/handlers/default.py               |   19 +++
 itm/handlers/example.py               |   14 ++
 itm/handlers/readdir_reply_nopages.py |   86 ++++++++++++
 itm/itm.py                            |  230 +++++++++++++++++++++++++++++++++
 itm/run_itm.sh                        |   41 ++++++
 itm/use_local.py                      |   14 ++
 9 files changed, 441 insertions(+), 0 deletions(-)
 create mode 100644 itm/README
 create mode 100644 itm/__init__.py
 create mode 100644 itm/handlers.py
 create mode 100644 itm/handlers/__init__.py
 create mode 100644 itm/handlers/default.py
 create mode 100644 itm/handlers/example.py
 create mode 100644 itm/handlers/readdir_reply_nopages.py
 create mode 100755 itm/itm.py
 create mode 100755 itm/run_itm.sh
 create mode 100644 itm/use_local.py
#4 lore raw

PATCH 3/3 itm: add a handler that truncates READDIR response page data

---
 itm/handlers/readdir_reply_nopages.py |   86 +++++++++++++++++++++++++++++++++
 1 files changed, 86 insertions(+), 0 deletions(-)
 create mode 100644 itm/handlers/readdir_reply_nopages.py
itm/handlers/readdir_reply_nopages.py new file +86 -0
new file mode 100644
index 0000000..e5dd91e
@@ -0,0 +1,86 @@
+from rpc_const import *
+from xdrdef.nfs4_const import *
+from xdrdef.nfs4_type import *
+from xdrdef.nfs3_const import *
+from xdrdef.nfs3_type import *
+from xdrdef.nfs3_pack import NFS3Packer, NFS3Unpacker
+from nfs4commoncode import encode_status_by_name
+from itm.handlers import BaseHandler
+
+import itm
+import nfs4lib
+
+"""
+   This handler modifies the response body for a v4 READDIR call
+   or v3 READDIRPLUS call such that there are no pages of data in
+   the response.
+
+   https://bugzilla.redhat.com/show_bug.cgi?id=1182830
+   http://marc.info/?l=linux-nfs&m=142964061807798&w=2
+   ce85cfb NFS: Don't attempt to decode missing directory entries
+"""
+
+class Handler(BaseHandler):
+
+	def will_handle(self, cb_info):
+
+		# setup for v4:
+		if hasattr(cb_info, "v4_res"):
+			res = cb_info.v4_res;
+			my_ops = filter(lambda x: x.resop == OP_READDIR, res.resarray)
+			if not my_ops:
+				return 0;
+
+			# save the resop for handle()
+			self.my_resop = my_ops[0]
+			return 1;
+
+		# setup for v3:
+		msg = cb_info.rpc_msg
+		if msg.body.mtype == REPLY and \
+			msg.body.cbody.vers == 3 and \
+			msg.body.cbody.proc == NFSPROC3_READDIRPLUS:
+
+			unpacker = NFS3Unpacker(cb_info.rpc_msg_data)
+			self.my_resop = unpacker.unpack_READDIRPLUS3res()
+
+			return 1;
+
+		return 0;
+
+	def handle(self, cb_info):
+		print "readdir is returning no pages"
+
+		msg = cb_info.rpc_msg
+
+		if msg.body.cbody.vers == 4:
+			# remove whatever results were being returned so that we have a
+			# predictable length:
+			self.my_resop.opreaddir.resok4.reply = dirlist4([], eof=0)
+
+			p = nfs4lib.FancyNFS4Packer()
+			p.pack_COMPOUND4res(cb_info.v4_res)
+			buf = p.get_buffer()
+
+			# chop off the page data:
+			cb_info.rpc_msg_data = buf[:-8]
+			#itm.dump_data(cb_info.rpc_msg_data)
+			cb_info.encode_RPC()
+			return 1
+
+		elif msg.body.cbody.vers == 3:
+			# remove whatever results were being returned so that we have a
+			# predictable length:
+			self.my_resop.resok.reply = dirlistplus3([], eof=0)
+
+			p = NFS3Packer()
+			p.pack_READDIRPLUS3res(self.my_resop)
+			buf = p.get_buffer()
+
+			# chop off the page data:
+			cb_info.rpc_msg_data = buf[:-8]
+			#itm.dump_data(cb_info.rpc_msg_data)
+			cb_info.encode_RPC()
+			return 1
+
+		return 0
Signature · 1 line
1.7.1
#5 lore raw
In reply to Benjamin Coddington
Quoted 4 lines · Benjamin Coddington

What follows is a small tool I think may be convenient to test and reproduce certain types of bugs that are difficult to create from above the filesystem, but are clearly problematic and have well-defined network triggers. Anna's recent BAD_STATEID on WRITES with delegation is a good

*Olga

Apologies.

Ben

Quoted 37 lines

example of that. This tool uses netfilters NFQUEUE target to allow a linux host to modify the NFS network traffic between existing clients and servers. In that sense, it is very similar to nfs-proxy, however I find it to be much more convenient to use, as it can be quickly inserted and removed from an existing network conection.

Benjamin Coddington (3):
Fix default arg order error on swig > 1.x
Add a tool for modification of NFS network traffic: itm
itm: add a handler that truncates READDIR response page data

 gssapi/gssapi.i                       |    2 +
 itm/README                            |   26 ++++
 itm/handlers.py                       |    9 ++
 itm/handlers/default.py               |   19 +++
 itm/handlers/example.py               |   14 ++
 itm/handlers/readdir_reply_nopages.py |   86 ++++++++++++
 itm/itm.py                            |  230 +++++++++++++++++++++++++++++++++
 itm/run_itm.sh                        |   41 ++++++
 itm/use_local.py                      |   14 ++
 9 files changed, 441 insertions(+), 0 deletions(-)
 create mode 100644 itm/README
 create mode 100644 itm/__init__.py
 create mode 100644 itm/handlers.py
 create mode 100644 itm/handlers/__init__.py
 create mode 100644 itm/handlers/default.py
 create mode 100644 itm/handlers/example.py
 create mode 100644 itm/handlers/readdir_reply_nopages.py
 create mode 100755 itm/itm.py
 create mode 100755 itm/run_itm.sh
 create mode 100644 itm/use_local.py

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#6 lore raw
Quoted 7 lines · Benjamin Coddington
Quoted 4 lines · Benjamin Coddington

What follows is a small tool I think may be convenient to test and reproduce certain types of bugs that are difficult to create from above the filesystem, but are clearly problematic and have well-defined network triggers. Anna's recent BAD_STATEID on WRITES with delegation is a good

Quoted 11 lines

*Olga

Apologies.

Ben

Quoted 5 lines

example of that. This tool uses netfilters NFQUEUE target to allow a linux host to modify the NFS network traffic between existing clients and servers. In that sense, it is very similar to nfs-proxy, however I find it to be much more convenient to use, as it can be quickly inserted and removed from an existing network conection.

By the way, I only recently noticed there's a branch of Fred's old pynfs repo with the proxy-nfs code. I've just merged that branch into my tree. (Let me know if anyone uses that.)

Do you want me to take these patches to? Do you think you're going to continue using this?

--b.

#7 lore raw
Quoted 20 lines · J. Bruce Fields
Quoted 7 lines · Benjamin Coddington
Quoted 4 lines · Benjamin Coddington

What follows is a small tool I think may be convenient to test and reproduce certain types of bugs that are difficult to create from above the filesystem, but are clearly problematic and have well-defined network triggers. Anna's recent BAD_STATEID on WRITES with delegation is a good

Quoted 11 lines

*Olga

Apologies.

Ben

Quoted 5 lines

example of that. This tool uses netfilters NFQUEUE target to allow a linux host to modify the NFS network traffic between existing clients and servers. In that sense, it is very similar to nfs-proxy, however I find it to be much more convenient to use, as it can be quickly inserted and removed from an existing network conection.

Quoted 6 lines

By the way, I only recently noticed there's a branch of Fred's old pynfs repo with the proxy-nfs code. I've just merged that branch into my tree. (Let me know if anyone uses that.)

Do you want me to take these patches to? Do you think you're going to continue using this?

I'm actually not sure yet if this is more or less useful than the
nfs-proxy.. In using it the past week, I found it to be convenient for
quickly inserting and removing behaviors, and then modifiying those behaviors and quickly inserting/removing them again. I think doing that with the nfs-proxy would be more disruptive to the client, potentially.

It does suffer from a scrambling of TCP sequencing if payload sizes are modified - the nfs-proxy doesn't have this problem. That causes the transport to want to reconnect.. so when using it you have to keep TCP in mind.

I think I'll continue to use what I have and do a bit of refinement and post back again in a bit.

Ben

#8 lore raw
Quoted 35 lines · Benjamin Coddington
Quoted 20 lines · J. Bruce Fields
Quoted 7 lines · Benjamin Coddington
Quoted 4 lines · Benjamin Coddington

What follows is a small tool I think may be convenient to test and reproduce certain types of bugs that are difficult to create from above the filesystem, but are clearly problematic and have well-defined network triggers. Anna's recent BAD_STATEID on WRITES with delegation is a good

Quoted 11 lines

*Olga

Apologies.

Ben

Quoted 5 lines

example of that. This tool uses netfilters NFQUEUE target to allow a linux host to modify the NFS network traffic between existing clients and servers. In that sense, it is very similar to nfs-proxy, however I find it to be much more convenient to use, as it can be quickly inserted and removed from an existing network conection.

Quoted 6 lines

By the way, I only recently noticed there's a branch of Fred's old pynfs repo with the proxy-nfs code. I've just merged that branch into my tree. (Let me know if anyone uses that.)

Do you want me to take these patches to? Do you think you're going to continue using this?

I'm actually not sure yet if this is more or less useful than the
nfs-proxy.. In using it the past week, I found it to be convenient for
quickly inserting and removing behaviors, and then modifiying those behaviors and quickly inserting/removing them again. I think doing that with the nfs-proxy would be more disruptive to the client, potentially.

Quoted 4 lines

It does suffer from a scrambling of TCP sequencing if payload sizes are modified - the nfs-proxy doesn't have this problem. That causes the transport to want to reconnect.. so when using it you have to keep TCP in mind.

Sounds a little scary.

Quoted 2 lines

I think I'll continue to use what I have and do a bit of refinement and post back again in a bit.

OK.

--b.

Acked-by on v1 1/3
#2 lore raw

Re: [PATCH pynfs 1/3] Fix default arg order error on swig > 1.x

In reply to Benjamin Coddington
Quoted 8 lines · Benjamin Coddington

The gssapi.py module ends up with
def initSecContext(name, context=None, INPUT=0, cred=0, mech, flags=2, time=0, chan=0):

which creates the error "SyntaxError: non-default argument follows default argument" on import. Revert to earlier swig behavior of using compact default args.

Anyway this one is useful.

thanks,
Kinglong Mee

Quoted 25 lines
---
 gssapi/gssapi.i |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
gssapi/gssapi.i +2 -0
index 5b66ce0..28656c7 100644
@@ -487,6 +487,7 @@ OM_uint32 reordered_init_sec_context
 %apply gss_buffer_t OUTPUT {gss_buffer_t token};
 %apply OM_uint32 *OUTPUT {OM_uint32 *flags, OM_uint32 *time};
 %feature("autodoc", "initSecContext(gss_name_t name, gss_ctx_id_t *context=None, string token=None, gss_cred_id_t cred=None, gss_OID mech=krb5oid, int flags=0, int time=0, gss_channel_bindings_t chan=None) -> context, mech, token, flags, time");
+%feature("compactdefaultargs");
 OM_uint32 reordered_init_sec_context
 	        (OM_uint32 *minor,		/* minor_status */
 		 gss_name_t name,			/* target_name IN*/
@@ -507,6 +508,7 @@ OM_uint32 reordered_init_sec_context
 %clear gss_OID *mech;
 %clear gss_buffer_t token;
 %clear OM_uint32 *flags, OM_uint32 *time;
+%feature("compactdefaultargs", 0);
 
 /********/