diff --git a/CHANGELOG b/CHANGELOG
index d69068bd7..23f8a2b0b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Improved error handling and reporting and re-designed communication
+ class in RPC library with patch from Djalal Harouni. [Patrik]
+
o Upgraded the included libpcap to version 1.1.1. [David]
o [NSE] Add some special use IPv4 addresses to isPrivate which are described in
diff --git a/nselib/rpc.lua b/nselib/rpc.lua
index 8f20a203c..942fd43b0 100644
--- a/nselib/rpc.lua
+++ b/nselib/rpc.lua
@@ -12,7 +12,9 @@
-- --------
-- The library contains the following classes:
-- o Comm
+-- - Handles network connections
-- - Handles low-level packet sending, recieving, decoding and encoding
+-- - Stores rpc programs info: socket, protocol, program name, id and version
-- - Used by Mount, NFS, RPC and Portmap
-- o Mount
-- - Handles communication with the mount RPC program
@@ -93,18 +95,32 @@ require("datafiles")
-- Revised 02/22/2010 - v0.2 - cleanup, revised the way TCP/UDP are handled fo
-- encoding an decoding
-- Revised 03/13/2010 - v0.3 - re-worked library to be OO
+-- Revised 04/18/2010 - v0.4 - Applied patch from Djalal Harouni with improved
+-- error checking and re-designed Comm class. see:
+-- http://seclists.org/nmap-dev/2010/q2/232
--
+-- RPC args using the nmap.registry.args
+RPC_args = {
+ ["rpcbind"] = { proto = 'rpc.protocol' },
+ ["nfs"] = { ver = 'nfs.version' },
+ ["mountd"] = { ver = 'mount.version' },
+}
+
+
-- Defines the order in which to try to connect to the RPC programs
-- TCP appears to be more stable than UDP in most cases, so try it first
-local RPC_PROTOCOLS = ( nmap.registry.args and nmap.registry.args['rpc.protocol'] and type(nmap.registry.args['rpc.protocol']) == 'table') and nmap.registry.args['rpc.protocol'] or { "tcp", "udp" }
+local RPC_PROTOCOLS = (nmap.registry.args and nmap.registry.args[RPC_args['rpcbind'].proto] and
+ type(nmap.registry.args[RPC_args['rpcbind'].proto]) == 'table') and
+ nmap.registry.args[RPC_args['rpcbind'].proto] or { "tcp", "udp" }
-- used to cache the contents of the rpc datafile
local RPC_PROGRAMS
-- Supported protocol versions
-Version = {
+RPC_version = {
+ ["rpcbind"] = { min=2, max=2 },
["nfs"] = { min=1, max=3 },
["mountd"] = { min=1, max=3 },
}
@@ -114,13 +130,98 @@ math.randomseed( os.time() )
-- Low-level communication class
Comm = {
- new = function(self,o)
- o = o or {}
- setmetatable(o, self)
+ --- Creats a new rpc Comm object
+ --
+ -- @param program name string
+ -- @param version number containing the program version to use
+ -- @return a new Comm object
+ new = function(self, program, version)
+ local o = {}
+ setmetatable(o, self)
self.__index = self
+ o.program = program
+ o.program_id = Util.ProgNameToNumber(program)
+ o:SetVersion(version)
return o
- end,
-
+ end,
+
+ --- Connects to the remote program
+ --
+ -- @param host table
+ -- @param port table
+ -- @return status boolean true on success, false on failure
+ -- @return string containing error message (if status is false)
+ Connect = function(self, host, port)
+ local status, err, socket
+ status, err = self:ChkProgram()
+ if (not(status)) then
+ return status, err
+ end
+ status, err = self:ChkVersion()
+ if (not(status)) then
+ return status, err
+ end
+ socket = nmap.new_socket()
+ status, err = socket:connect(host.ip, port.number, port.protocol)
+ if (not(status)) then
+ return status, string.format("%s connect error: %s", self.program, err)
+ else
+ self.socket = socket
+ self.ip = host.ip
+ self.port = port.number
+ self.proto = port.protocol
+ return status, nil
+ end
+ end,
+
+ --- Disconnects from the remote program
+ --
+ -- @return status boolean true on success, false on failure
+ -- @return string containing error message (if status is false)
+ Disconnect = function(self)
+ local status, err = self.socket:close()
+ if (not(status)) then
+ return status, string.format("%s disconnect error: %s", self.program, err)
+ end
+ self.socket=nil
+ return status, nil
+ end,
+
+ --- Checks if the rpc program is supported
+ --
+ -- @return status boolean true on success, false on failure
+ -- @return string containing error message (if status is false)
+ ChkProgram = function(self)
+ if (not(RPC_version[self.program])) then
+ return false, string.format("RPC library does not support: %s protocol", self.program)
+ end
+ return true, nil
+ end,
+
+ --- Checks if the rpc program version is supported
+ --
+ -- @return status boolean true on success, false on failure
+ -- @return string containing error message (if status is false)
+ ChkVersion = function(self)
+ if ( self.version > RPC_version[self.program].max or self.version < RPC_version[self.program].min ) then
+ return false, string.format("RPC library does not support: %s version %d",self.program,self.version)
+ end
+ return true, nil
+ end,
+
+ --- Sets the rpc program version
+ --
+ -- @return status boolean true
+ SetVersion = function(self, version)
+ if (RPC_version[self.program] and RPC_args[self.program] and
+ nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then
+ self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver])
+ elseif (not(self.version) and version) then
+ self.version = version
+ end
+ return true, nil
+ end,
+
--- Checks if data contains enough bytes to read the needed amount
-- If it doesn't it attempts to read the remaining amount of bytes from the socket
--
@@ -128,11 +229,10 @@ Comm = {
-- @param pos number containing the current offset into the buffer
-- @param needed number containing the number of bytes needed to be available
-- @return status success or failure
- -- @return data string containing the data passed to the function and the additional data appended to it
+ -- @return data string containing the data passed to the function and the additional data appended to it or error message on failure
GetAdditionalBytes = function( self, data, pos, needed )
- local status = true
- local tmp
+ local status, tmp
if data:len() - pos + 1 < needed then
local toread = needed - ( data:len() - pos + 1 )
@@ -143,18 +243,17 @@ Comm = {
return false, string.format("getAdditionalBytes() failed to read: %d bytes from the socket", needed - ( data:len() - pos ) )
end
end
- return status, data
+ return true, data
end,
--- Creates a RPC header
--
-- @param xid number
- -- @param program_id number containing the program_id to connect to
- -- @param program_version number containing the version to query
-- @param procedure number containing the procedure to call
-- @param auth table containing the authentication data to use
- -- @return string of bytes
- CreateHeader = function( self, xid, program_id, program_version, procedure, auth )
+ -- @return status boolean true on success, false on failure
+ -- @return string of bytes on success, error message on failure
+ CreateHeader = function( self, xid, procedure, auth )
local RPC_VERSION = 2
local packet
@@ -162,10 +261,10 @@ Comm = {
xid = math.random(1234567890)
end
if not auth or auth.type ~= RPC.AuthType.Null then
- return false, "No or invalid authentication type specified"
+ return false, "Comm.CreateHeader: No or invalid authentication type specified"
end
- packet = bin.pack( ">IIIIII", xid, RPC.MessageType.Call, RPC_VERSION, program_id, program_version, procedure )
+ packet = bin.pack( ">IIIIII", xid, RPC.MessageType.Call, RPC_VERSION, self.program_id, self.version, procedure )
if auth.type == RPC.AuthType.Null then
packet = packet .. bin.pack( "IIII", 0, 0, 0, 0 )
end
@@ -178,7 +277,7 @@ Comm = {
-- @param pos number containing the current offset into data
-- @return pos number containing the offset after the decoding
-- @return header table containing xid, type, state,
- -- verifier and accept_state
+ -- verifier and ( accept_state or denied_state )
DecodeHeader = function( self, data, pos )
local header = {}
local status
@@ -191,32 +290,43 @@ Comm = {
local tmp
status, tmp = self:GetAdditionalBytes( data, pos, HEADER_LEN - ( data:len() - pos ) )
if not status then
+ stdnse.print_debug(string.format("Comm.ReceivePacket: failed to call GetAdditionalBytes"))
return -1, nil
end
data = data .. tmp
end
pos, header.xid, header.type, header.state = bin.unpack(">III", data, pos)
+
+ if ( header.state == RPC.State.Denied ) then
+ pos, header.denied_state = bin.unpack(">I", data, pos )
+ return pos, header
+ end
+
pos, header.verifier.flavor = bin.unpack(">I", data, pos)
pos, header.verifier.length = bin.unpack(">I", data, pos)
if header.verifier.length - 8 > 0 then
status, data = self:GetAdditionalBytes( data, pos, header.verifier.length - 8 )
if not status then
+ stdnse.print_debug(string.format("Comm.ReceivePacket: failed to call GetAdditionalBytes"))
return -1, nil
end
pos, header.verifier.data = bin.unpack("A" .. header.verifier.length - 8, data, pos )
end
pos, header.accept_state = bin.unpack(">I", data, pos )
+
+
return pos, header
end,
--- Reads the response from the socket
--
- -- @return data string containing the raw response
+ -- @return status true on success, false on failure
+ -- @return data string containing the raw response or error message on failure
ReceivePacket = function( self )
local status
-
+
if ( self.proto == "udp" ) then
-- There's not much we can do in here to check if we received all data
-- as the packet contains no length field. It's up to each decoding function
@@ -230,9 +340,9 @@ Comm = {
lastfragment = false
status, data = self:GetAdditionalBytes( data, pos, 4 )
if ( not(status) ) then
- return false, "rpc.Comm.ReceivePacket: failed to call GetAdditionalBytes"
+ return false, "Comm.ReceivePacket: failed to call GetAdditionalBytes"
end
-
+
pos, tmp = bin.unpack(">i", data, pos )
length = bit.band( tmp, 0x7FFFFFFF )
@@ -242,7 +352,7 @@ Comm = {
status, data = self:GetAdditionalBytes( data, pos, length )
if ( not(status) ) then
- return false, "rpc.Comm.ReceivePacket: failed to call GetAdditionalBytes"
+ return false, "Comm.ReceivePacket: failed to call GetAdditionalBytes"
end
--
@@ -279,20 +389,18 @@ Comm = {
--- Encodes a RPC packet
--
-- @param xid number containing the transaction ID
- -- @param prog number containing the program id
+ -- @param procedure number containing the procedure to call
-- @param auth table containing authentication information
-- @param data string containing the packet data
-- @return packet string containing the encoded packet data
- EncodePacket = function( self, xid, prog, auth, data )
- local status, packet = self:CreateHeader( xid, prog.id, prog.version, prog.proc, auth )
+ EncodePacket = function( self, xid, proc, auth, data )
+ local status, packet = self:CreateHeader( xid, proc, auth )
local len
-
if ( not(status) ) then
return
end
packet = packet .. ( data or "" )
-
if ( self.proto == "udp") then
return packet
else
@@ -305,8 +413,9 @@ Comm = {
SendPacket = function( self, packet )
return self.socket:send( packet )
end,
-
+
}
+
--- Mount class handling communication with the mountd program
--
@@ -315,6 +424,31 @@ Comm = {
--
Mount = {
+ StatMsg = {
+ [1] = "Not owner.",
+ [2] = "No such file or directory.",
+ [5] = "I/O error.",
+ [13] = "Permission denied.",
+ [20] = "Not a directory.",
+ [22] = "Invalid argument.",
+ [63] = "Filename too long.",
+ [10004] = "Operation not supported.",
+ [10006] = "A failure on the server.",
+ },
+
+ StatCode = {
+ MNT_OK = 0,
+ MNTERR_PERM = 1,
+ MNTERR_NOENT = 2,
+ MNTERR_IO = 5,
+ MNTERR_ACCES = 13,
+ MNTERR_NOTDIR = 20,
+ MNTERR_INVAL = 22,
+ MNTERR_NAMETOOLONG = 63,
+ MNTERR_NOTSUPP = 10004,
+ MNTERR_SERVERFAULT = 10006,
+ },
+
Procedure =
{
MOUNT = 1,
@@ -329,57 +463,17 @@ Mount = {
setmetatable(o, self)
self.__index = self
return o
- end,
-
- --- Connects to the mountd program
- --
- -- @param host table
- -- @param port table
- -- @param version number containing the program version to use
- -- @return status boolean true on success, false on failure
- -- @return result string containing error message (if status is false)
- Connect = function( self, host, port, version )
- local socket = nmap.new_socket()
- local status, result = socket:connect(host.ip, port.number, port.protocol)
-
- if ( status ) then
- self.socket = socket
- self.proto = port.protocol
- self.comm = Comm:new( { socket = socket, proto=port.protocol} )
- self.version = ( nmap.registry.args and nmap.registry.args['mount.version'] ) and tonumber(nmap.registry.args['mount.version']) or version
-
- if ( self.version > Version["mountd"].max or self.version < Version["mountd"].min ) then
- return false, "Library does not support mountd version: " .. self.version
- end
- end
-
- return status, result
- end,
-
- --- Disconnects from the mountd program
- --
- -- @return status boolean true on success, false on failure
- -- @return result string containing error message (if status is false)
- Disconnect = function( self )
- local status, result = self.socket:close()
- if ( status ) then
- self.proto = nil
- self.socket = nil
- self.comm = nil
- end
- return status, result
end,
--- Requests a list of NFS export from the remote server
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @return status success or failure
-- @return entries table containing a list of share names (strings)
- Export = function( self )
+ Export = function(self, comm)
- local catch = function() self.socket:close() end
- local try = nmap.new_try(catch)
local msg_type = 0
- local prg_mount = Util.ProgNameToNumber("mountd")
local packet
local pos = 1
local header = {}
@@ -387,37 +481,43 @@ Mount = {
local data = ""
local status
- local REPLY_ACCEPTED, SUCCESS, PROC_EXPORT = 0, 0, 5
-
- if self.proto ~= "tcp" and self.proto ~= "udp" then
- return false, "Protocol should be either udp or tcp"
+ if comm.proto ~= "tcp" and comm.proto ~= "udp" then
+ return false, "Mount.Export: Protocol should be either udp or tcp"
+ end
+ packet = comm:EncodePacket(nil, Mount.Procedure.EXPORT, { type=RPC.AuthType.Null }, nil )
+ if (not(comm:SendPacket( packet ))) then
+ return false, "Mount.Export: Failed to send data"
end
- packet = self.comm:EncodePacket( nil, { id=prg_mount, version=self.version, proc=Mount.Procedure.EXPORT }, { type=RPC.AuthType.Null }, nil )
- try( self.comm:SendPacket( packet ) )
- status, data = self.comm:ReceivePacket()
+ status, data = comm:ReceivePacket()
if ( not(status) ) then
- return false, "mountExportCall: Failed to read data from socket"
+ return false, "Mount.Export: Failed to read data from socket"
end
-- make sure we have atleast 24 bytes to unpack the header
- data = try( self.comm:GetAdditionalBytes( data, pos, 24 ) )
- pos, header = self.comm:DecodeHeader( data, pos )
-
+ status, data = comm:GetAdditionalBytes( data, pos, 24 )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
+ pos, header = comm:DecodeHeader( data, pos )
if not header then
- return false, "Failed to decode header"
+ return false, "Mount.Export: Failed to decode header"
end
if header.type ~= RPC.MessageType.Reply then
- return false, string.format("Packet was not a reply")
+ return false, "Mount.Export: packet was not a reply"
end
- if header.state ~= REPLY_ACCEPTED then
- return false, string.format("Reply state was not Accepted(0) as expected")
+ if header.state ~= RPC.State.Accepted then
+ if ( header.denied_state == RPC.RejectState.AUTH_ERROR ) then
+ return false, "Mount.Export: RPC Authentication Failed"
+ else
+ return false, "Mount.Export: Reply state was not Accepted(0) as expected"
+ end
end
- if header.accept_state ~= SUCCESS then
- return false, string.format("Accept State was not Successful")
+ if header.accept_state ~= RPC.AcceptState.SUCCESS then
+ return false, "Mount.Export: Accept State was not Successful"
end
---
@@ -438,7 +538,10 @@ Mount = {
---
while true do
-- make sure we have atleast 4 more bytes to check for value follows
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
local data_follows
pos, data_follows = bin.unpack( ">I", data, pos )
@@ -452,10 +555,16 @@ Mount = {
local len
-- make sure we have atleast 4 more bytes to get the length
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
pos, len = bin.unpack(">I", data, pos )
- data = try( self.comm:GetAdditionalBytes( data, pos, len ) )
+ status, data = comm:GetAdditionalBytes( data, pos, len )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
pos, entry.name = bin.unpack("A" .. len, data, pos )
pos = pos + Util.CalcFillBytes( len )
@@ -463,16 +572,25 @@ Mount = {
while true do
local group
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
pos, data_follows = bin.unpack( ">I", data, pos )
if data_follows ~= 1 then
break
end
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
pos, len = bin.unpack( ">I", data, pos )
- data = try( self.comm:GetAdditionalBytes( data, pos, len ) )
+ status, data = comm:GetAdditionalBytes( data, pos, len )
+ if (not(status)) then
+ return false, "Mount.Export: Failed to call GetAdditionalBytes"
+ end
pos, group = bin.unpack( "A" .. len, data, pos )
table.insert( entry, group )
@@ -483,73 +601,85 @@ Mount = {
return true, entries
end,
-
--- Attempts to mount a remote export in order to get the filehandle
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param path string containing the path to mount
-- @return status success or failure
-- @return fhandle string containing the filehandle of the remote export
- Mount = function( self, path )
-
- local catch = function() self.socket:close() end
- local try = nmap.new_try(catch)
- local packet, data
- local prog_id = Util.ProgNameToNumber("mountd")
+ Mount = function(self, comm, path)
+ local packet, mount_status
local _, pos, data, header, fhandle = "", 1, "", "", {}
local status, len
- local REPLY_ACCEPTED, SUCCESS, MOUNT_OK = 0, 0, 0
-
data = bin.pack(">IA", path:len(), path)
for i=1, Util.CalcFillBytes( path:len() ) do
data = data .. string.char( 0x00 )
end
- packet = self.comm:EncodePacket( nil, { id=prog_id, version=self.version, proc=Mount.Procedure.MOUNT }, { type=RPC.AuthType.Null }, data )
- try( self.comm:SendPacket( packet ) )
-
- status, data = self.comm:ReceivePacket()
- if ( not(status) ) then
- return false, "mountCall: Failed to read data from socket"
+ packet = comm:EncodePacket( nil, Mount.Procedure.MOUNT, { type=RPC.AuthType.Null }, data )
+ if (not(comm:SendPacket(packet))) then
+ return false, "Mount: Failed to send data"
end
- pos, header = self.comm:DecodeHeader( data, pos )
+ status, data = comm:ReceivePacket()
+ if ( not(status) ) then
+ return false, "Mount: Failed to read data from socket"
+ end
+
+ pos, header = comm:DecodeHeader( data, pos )
if not header then
- return false, "Failed to decode header"
+ return false, "Mount: Failed to decode header"
end
if header.type ~= RPC.MessageType.Reply then
- return false, string.format("Packet was not a reply")
+ return false, "Mount: Packet was not a reply"
end
- if header.state ~= REPLY_ACCEPTED then
- return false, string.format("Reply state was not Accepted(0) as expected")
- end
-
- if header.accept_state ~= SUCCESS then
- return false, string.format(3, "mountCall: Accept State was not Successful", path)
- end
-
- local mount_status
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
- pos, mount_status = bin.unpack(">I", data, pos )
-
- if mount_status ~= MOUNT_OK then
- if ( mount_status == 13 ) then
- return false, "Access Denied"
+ if header.state ~= RPC.State.Accepted then
+ if ( header.denied_state == RPC.RejectState.AUTH_ERROR ) then
+ return false, "Mount: RPC Authentication Failed"
else
- return false, string.format("Mount failed: %d", mount_status)
+ return false, "Mount: Reply state was not Accepted(0) as expected"
end
end
- if ( self.version == 3 ) then
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
+ if header.accept_state ~= RPC.AcceptState.SUCCESS then
+ return false, string.format(3, "Mount: Accept State was not Successful", path)
+ end
+
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ return false, "Mount: Failed to call GetAdditionalBytes"
+ end
+ pos, mount_status = bin.unpack(">I", data, pos )
+
+ if (mount_status ~= Mount.StatCode.MNT_OK) then
+ if (Mount.StatMsg[mount_status]) then
+ return false, string.format("Mount failed: %s",Mount.StatMsg[mount_status])
+ else
+ return false, string.format("Mount failed: code %d", mount_status)
+ end
+ end
+
+ if ( comm.version == 3 ) then
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ return false, "Mount: Failed to call GetAdditionalBytes"
+ end
_, len = bin.unpack(">I", data, pos )
- data = try( self.comm:GetAdditionalBytes( data, pos, len + 4 ) )
+ status, data = comm:GetAdditionalBytes( data, pos, len + 4 )
+ if (not(status)) then
+ return false, "Mount: Failed to call GetAdditionalBytes"
+ end
pos, fhandle = bin.unpack( "A" .. len + 4, data, pos )
- elseif ( self.version < 3 ) then
- data = try( self.comm:GetAdditionalBytes( data, pos, 32 ) )
+ elseif ( comm.version < 3 ) then
+ status, data = comm:GetAdditionalBytes( data, pos, 32 )
+ if (not(status)) then
+ return false, "Mount: Failed to call GetAdditionalBytes"
+ end
pos, fhandle = bin.unpack( "A32", data, pos )
else
return false, "Mount failed"
@@ -560,19 +690,14 @@ Mount = {
--- Attempts to unmount a remote export in order to get the filehandle
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param path string containing the path to mount
-- @return status success or failure
-- @return error string containing error if status is false
- Unmount = function( self, path )
-
- local catch = function() self.socket:close() end
- local try = nmap.new_try(catch)
- local packet, data
- local prog_id = Util.ProgNameToNumber("mountd")
+ Unmount = function(self, comm, path)
+ local packet, status
local _, pos, data, header, fhandle = "", 1, "", "", {}
- local status
-
- local REPLY_ACCEPTED, SUCCESS, MOUNT_OK = 0, 0, 0
data = bin.pack(">IA", path:len(), path)
@@ -580,29 +705,31 @@ Mount = {
data = data .. string.char( 0x00 )
end
- packet = self.comm:EncodePacket( nil, { id=prog_id, version=self.version, proc=Mount.Procedure.UMNT }, { type=RPC.AuthType.Null }, data )
- try( self.comm:SendPacket( packet ) )
-
- status, data = self.comm:ReceivePacket( )
- if ( not(status) ) then
- return false, "mountCall: Failed to read data from socket"
+ packet = comm:EncodePacket( nil, Mount.Procedure.UMNT, { type=RPC.AuthType.Null }, data )
+ if (not(comm:SendPacket(packet))) then
+ return false, "Unmount: Failed to send data"
end
- pos, header = self.comm:DecodeHeader( data, pos )
+ status, data = comm:ReceivePacket( )
+ if ( not(status) ) then
+ return false, "Unmount: Failed to read data from socket"
+ end
+
+ pos, header = comm:DecodeHeader( data, pos )
if not header then
- return false, "Failed to decode header"
+ return false, "Unmount: Failed to decode header"
end
if header.type ~= RPC.MessageType.Reply then
- return false, string.format("Packet was not a reply")
+ return false, "Unmount: Packet was not a reply"
end
- if header.state ~= REPLY_ACCEPTED then
- return false, string.format("Reply state was not Accepted(0) as expected")
+ if header.state ~= RPC.State.Accepted then
+ return false, "Unmount: Reply state was not Accepted(0) as expected"
end
- if header.accept_state ~= SUCCESS then
- return false, string.format(3, "mountCall: Accept State was not Successful", path)
+ if header.accept_state ~= RPC.AcceptState.SUCCESS then
+ return false, string.format(3, "Unmount: Accept State was not Successful", path)
end
return true, ""
@@ -617,6 +744,118 @@ Mount = {
--
NFS = {
+ -- NFS error msg v2 and v3
+ StatMsg = {
+ [1] = "Not owner.",
+ [2] = "No such file or directory.",
+ [5] = "I/O error.",
+ [6] = "I/O error. No such device or address.",
+ [13] = "Permission denied.",
+ [17] = "File exists.",
+ [18] = "Attempt to do a cross-device hard link.",
+ [19] = "No such device.",
+ [20] = "Not a directory.",
+ [21] = "Is a directory.",
+ [22] = "Invalid argument or unsupported argument for an operation.",
+ [27] = "File too large.",
+ [28] = "No space left on device.",
+ [30] = "Read-only file system.",
+ [31] = "Too many hard links.",
+ [63] = "The filename in an operation was too long.",
+ [66] = "An attempt was made to remove a directory that was not empty.",
+ [69] = "Resource (quota) hard limit exceeded.",
+ [70] = "Invalid file handle.",
+ [71] = "Too many levels of remote in path.",
+ [99] = "The server's write cache used in the \"WRITECACHE\" call got flushed to disk.",
+ [10001] = "Illegal NFS file handle.",
+ [10002] = "Update synchronization mismatch was detected during a SETATTR operation.",
+ [10003] = "READDIR or READDIRPLUS cookie is stale.",
+ [10004] = "Operation is not supported.",
+ [10005] = "Buffer or request is too small.",
+ [10006] = "An error occurred on the server which does not map to any of the legal NFS version 3 protocol error values.",
+ [10007] = "An attempt was made to create an object of a type not supported by the server.",
+ [10008] = "The server initiated the request, but was not able to complete it in a timely fashion.",
+ },
+
+ StatCode = {
+ -- NFS Version 1
+ [1] = {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_WFLUSH = 99,
+ },
+
+ -- NFS Version 2
+ [2] = {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_WFLUSH = 99,
+ },
+
+ -- NFS Version 3
+ [3] = {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_XDEV = 18,
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22,
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_MLINK = 31,
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_REMOTE = 71,
+ NFSERR_BADHANDLE = 10001,
+ NFSERR_NOT_SYNC = 10002,
+ NFSERR_BAD_COOKIE = 10003,
+ NFSERR_NOTSUPP = 10004,
+ NFSERR_TOOSMALL = 10005,
+ NFSERR_SERVERFAULT = 10006,
+ NFSERR_BADTYPE = 10007,
+ NFSERR_JUKEBOX = 10008,
+ },
+ },
+
-- Unfortunately the NFS procedure numbers differ in between versions
Procedure =
{
@@ -664,50 +903,12 @@ NFS = {
setmetatable(o, self)
self.__index = self
return o
- end,
+ end,
- --- Connects to the nfsd program
- --
- -- @param host table
- -- @param port table
- -- @param version number containing the program version to use
- -- @return status boolean true on success, false on failure
- -- @return result string containing error message (if status is false)
- Connect = function( self, host, port, version )
- local socket = nmap.new_socket()
- local status, result = socket:connect(host.ip, port.number, port.protocol)
-
- if ( status ) then
- self.socket = socket
- self.proto = port.protocol
- self.version = ( nmap.registry.args and nmap.registry.args['nfs.version'] ) and tonumber(nmap.registry.args['nfs.version']) or version
-
- if ( self.version > Version["nfs"].max or self.version < Version["nfs"].min ) then
- return false, "Library does not support nfsd version: " .. self.version
- end
-
- self.comm = Comm:new( { socket = socket, proto=port.protocol} )
- end
-
- return status, result
- end,
-
- --- Disconnects from the nfsd program
- --
- -- @return status boolean true on success, false on failure
- -- @return result string containing error message (if status is false)
- Disconnect = function( self )
- local status, result = self.socket:close()
- if ( status ) then
- self.proto = nil
- self.socket = nil
- self.comm = nil
- end
- return status, result
- end,
-
- --- Decodes the READDIR section of a NFS ReadDir response
+ --- Decodes the READDIR section of a NFS ReadDir response
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param data string containing the buffer of bytes read so far
-- @param pos number containing the current offset into data
-- @return pos number containing the offset after the decoding
@@ -717,47 +918,54 @@ NFS = {
-- table for each file/directory entry. It has the following fields
-- file_id, name and cookie
--
- ReadDirDecode = function( self, data, pos )
-
+ ReadDirDecode = function( self, comm, data, pos )
local entry, response = {}, {}
local value_follows
local status, _
- local NFS_OK = 0
-
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, status = bin.unpack(">I", data, pos)
- if status ~= NFS_OK then
+ if (status ~= NFS.StatCode[comm.version].NFS_OK) then
+ if (NFS.StatMsg[status]) then
+ stdnse.print_debug(string.format("READDIR query failed: %s", NFS.StatMsg[status]))
+ else
+ stdnse.print_debug(string.format("READDIR query failed: code %d", status))
+ end
return -1, nil
end
- if ( 3 == self.version ) then
+ if ( 3 == comm.version ) then
local attrib = {}
response.attributes = {}
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if( not(status) ) then
- return false, "NFS.ReadDirDecode failed to get additional bytes from socket"
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
+
pos, value_follows = bin.unpack(">I", data, pos)
if value_follows == 0 then
return -1, nil
end
- status, data = self.comm:GetAdditionalBytes( data, pos, 84 )
- if( not(status) ) then
- return false, "NFS.ReadDirDecode failed to get additional bytes from socket"
+ status, data = comm:GetAdditionalBytes( data, pos, 84 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, attrib.type, attrib.mode, attrib.nlink, attrib.uid, attrib.gid,
attrib.size, attrib.used, attrib.rdev, attrib.fsid, attrib.fileid,
attrib.atime, attrib.mtime, attrib.ctime = bin.unpack(">IIIIILLLLLLLL", data, pos)
table.insert(response.attributes, attrib)
-- opaque data
- status, data = self.comm:GetAdditionalBytes( data, pos, 8 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, 8 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, _ = bin.unpack(">L", data, pos)
end
@@ -765,9 +973,10 @@ NFS = {
response.entries = {}
while true do
entry = {}
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, value_follows = bin.unpack(">I", data, pos)
@@ -776,61 +985,66 @@ NFS = {
break
end
- if ( 3 == self.version ) then
- status, data = self.comm:GetAdditionalBytes( data, pos, 8 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ if ( 3 == comm.version ) then
+ status, data = comm:GetAdditionalBytes( data, pos, 8 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, entry.fileid = bin.unpack(">L", data, pos )
else
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, entry.fileid = bin.unpack(">I", data, pos )
end
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, entry.length = bin.unpack(">I", data, pos)
- status, data = self.comm:GetAdditionalBytes( data, pos, entry.length )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, entry.length )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, entry.name = bin.unpack("A" .. entry.length, data, pos)
pos = pos + Util.CalcFillBytes( entry.length )
- if ( 3 == self.version ) then
- status, data = self.comm:GetAdditionalBytes( data, pos, 8 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ if ( 3 == comm.version ) then
+ status, data = comm:GetAdditionalBytes( data, pos, 8 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
-
pos, entry.cookie = bin.unpack(">L", data, pos)
else
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if ( not(status) ) then
- return false, "ReadDirDecode failed"
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
-
pos, entry.cookie = bin.unpack(">I", data, pos)
end
table.insert( response.entries, entry )
end
return pos, response
end,
-
-
+
--- Reads the contents inside a NFS directory
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param file_handle string containing the filehandle to query
-- @return status true on success, false on failure
-- @return table of file table entries as described in decodeReadDir
- ReadDir = function( self, file_handle )
+ ReadDir = function( self, comm, file_handle )
local status, packet
local cookie, count = 0, 8192
@@ -838,83 +1052,88 @@ NFS = {
local header, response = {}, {}
if ( not(file_handle) ) then
- return false, "No filehandle received"
+ return false, "ReadDir: No filehandle received"
end
- if ( self.version == 3 ) then
+ if ( comm.version == 3 ) then
local opaque_data = 0
data = bin.pack("A>L>L>I", file_handle, cookie, opaque_data, count)
else
data = bin.pack("A>I>I", file_handle, cookie, count)
end
- packet = self.comm:EncodePacket( nil, { id=Util.ProgNameToNumber("nfs"), version=self.version, proc=NFS.Procedure[self.version].READDIR }, { type=RPC.AuthType.Null }, data )
- status = self.comm:SendPacket( packet )
- if ( not(status) ) then
- return false, "nfsReadDir: Failed to write to socket"
- end
-
- status, data = self.comm:ReceivePacket()
- if ( not(status) ) then
- return false, "nfsReadDir: Failed to read data from socket"
+ packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].READDIR, { type=RPC.AuthType.Null }, data )
+ if(not(comm:SendPacket( packet ))) then
+ return false, "ReadDir: Failed to send data"
end
- pos, header = self.comm:DecodeHeader( data, pos )
- if not header then
- return false, "Failed to decode header"
+ status, data = comm:ReceivePacket()
+ if ( not(status) ) then
+ return false, "ReadDir: Failed to read data from socket"
+ end
+
+ pos, header = comm:DecodeHeader( data, pos )
+ if not header then
+ return false, "ReadDir: Failed to decode header"
+ end
+ pos, response = self:ReadDirDecode( comm, data, pos )
+ if (not(response)) then
+ return false, "ReadDir: Failed to decode the READDIR section"
end
- pos, response = self:ReadDirDecode( data, pos )
return true, response
end,
--- Gets filesystem stats (Total Blocks, Free Blocks and Available block) on a remote NFS share
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param file_handle string containing the filehandle to query
-- @return status true on success, false on failure
-- @return statfs table with the fields transfer_size, block_size,
-- total_blocks, free_blocks and available_blocks
-- @return errormsg if status is false
- StatFs = function( self, file_handle )
+ StatFs = function( self, comm, file_handle )
local status, packet
local pos, data, _ = 1, "", ""
local header, statfs = {}, {}
- if ( self.version > 2 ) then
- return false, ("Version %d not supported"):format(self.version)
+ if ( comm.version > 2 ) then
+ return false, ("StatFs: Version %d not supported"):format(comm.version)
end
if ( not(file_handle) or file_handle:len() ~= 32 ) then
- return false, "Incorrect filehandle received"
+ return false, "StatFs: Incorrect filehandle received"
end
data = bin.pack("A", file_handle )
- packet = self.comm:EncodePacket( nil, { id=Util.ProgNameToNumber("nfs"), version=self.version, proc=NFS.Procedure[self.version].STATFS }, { type=RPC.AuthType.Null }, data )
- status = self.comm:SendPacket( packet )
- if ( not(status) ) then
- return false, "nfsStatFs: Failed to write to socket"
- end
-
- status, data = self.comm:ReceivePacket( )
- if ( not(status) ) then
- return false, "nfsStatFs: Failed to read data from socket"
+ packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].STATFS, { type=RPC.AuthType.Null }, data )
+ if (not(comm:SendPacket( packet ))) then
+ return false, "StatFS: Failed to send data"
end
- pos, header = self.comm:DecodeHeader( data, pos )
+ status, data = comm:ReceivePacket( )
+ if ( not(status) ) then
+ return false, "StatFs: Failed to read data from socket"
+ end
+
+ pos, header = comm:DecodeHeader( data, pos )
if not header then
- return false, "Failed to decode header"
+ return false, "StatFs: Failed to decode header"
end
- pos, statfs = self:StatFsDecode( data, pos )
+ pos, statfs = self:StatFsDecode( comm, data, pos )
if not statfs then
- return false, "Failed to decode statfs structure"
+ return false, "StatFs: Failed to decode statfs structure"
end
return true, statfs
end,
--- Attempts to decode the attributes section of the reply
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param data string containing the full statfs reply
-- @param pos number pointing to the statfs section of the reply
-- @return pos number containing the offset after decoding
@@ -923,117 +1142,134 @@ NFS = {
-- blocksize, rdev, blocks, fsid,
-- fileid, atime, mtime and ctime
--
- GetAttrDecode = function( self, data, pos )
+ GetAttrDecode = function( self, comm, data, pos )
local attrib = {}
- local catch = function() self.socket:close() end
- local try = nmap.new_try(catch)
- local NFS_OK = 0
local status
- status, data = self.comm:GetAdditionalBytes( data, pos, 4 )
- if ( not(status) ) then
- return false, "GetAttrDecode: GetAdditionalBytes failed"
- end
-
- pos, attrib.status = bin.unpack(">I", data, pos)
-
- if attrib.status ~= NFS_OK then
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("GetAttrDecode: Failed to call GetAdditionalBytes")
return -1, nil
end
- if ( self.version < 3 ) then
- status, data = self.comm:GetAdditionalBytes( data, pos, 64 )
+ pos, attrib.status = bin.unpack(">I", data, pos)
+
+ if (attrib.status ~= NFS.StatCode[comm.version].NFS_OK) then
+ if (NFS.StatMsg[attrib.status]) then
+ stdnse.print_debug(string.format("GETATTR query failed: %s", NFS.StatMsg[attrib.status]))
+ else
+ stdnse.print_debug(string.format("GETATTR query failed: code %d", attrib.status))
+ end
+ return -1, nil
+ end
+
+ if ( comm.version < 3 ) then
+ status, data = comm:GetAdditionalBytes( data, pos, 64 )
if ( not(status) ) then
- return false, "GetAttrDecode: GetAdditionalBytes failed"
+ stdnse.print_debug("GetAttrDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
end
pos, attrib.type, attrib.mode, attrib.nlink, attrib.uid,
attrib.gid, attrib.size, attrib.blocksize, attrib.rdev,
attrib.blocks, attrib.fsid, attrib.fileid, attrib.atime,
attrib.mtime, attrib.ctime = bin.unpack( ">IIIIIIIIIILLL", data, pos )
- elseif ( self.version == 3 ) then
- status, data = self.comm:GetAdditionalBytes( data, pos, 84 )
- if ( not(status) ) then
- return false, "GetAttrDecode: GetAdditionalBytes failed"
- end
- pos, attrib.type, attrib.mode, attrib.nlink, attrib.uid,
- attrib.gid, attrib.size, attrib.used, attrib.rdev,
- attrib.fsid, attrib.fileid, attrib.atime, attrib.mtime,
- attrib.ctime = bin.unpack(">IIIIILLLLLLLL", data, pos)
- else
- return -1, "Unsupported version"
- end
- return pos, attrib
- end,
- --- Gets mount attributes (uid, gid, mode, etc ..) from a remote NFS share
- --
- -- @param file_handle string containing the filehandle to query
- -- @return status true on success, false on failure
- -- @return attribs table with the fields type, mode,
- -- nlink, uid, gid, size,
- -- blocksize, rdev, blocks, fsid,
- -- fileid, atime, mtime and ctime
- -- @return errormsg if status is false
- GetAttr = function( self, file_handle )
- local data, packet, status, attribs, pos, header
-
- data = bin.pack("A", file_handle)
- packet = self.comm:EncodePacket( nil, { id=Util.ProgNameToNumber("nfs"), version=self.version, proc=NFS.Procedure[self.version].GETATTR }, { type=RPC.AuthType.Null }, data )
- status = self.comm:SendPacket(packet)
- if ( not(status) ) then
- return false, "nfsGetAttribs: Failed to send data to socket"
- end
-
- status, data = self.comm:ReceivePacket()
- if ( not(status) ) then
- return false, "nfsGetAttribs: Failed to read data from socket"
- end
-
- pos, header = self.comm:DecodeHeader( data, 1 )
-
- if not header then
- return false, "Failed to decode header"
- end
-
- pos, attribs = self:GetAttrDecode( data, pos )
-
- if not attribs then
- return false, "Failed to decode attrib structure"
- end
-
- return true, attribs
- end,
-
- --- Attempts to decode the StatFS section of the reply
- --
- -- @param data string containing the full statfs reply
- -- @param pos number pointing to the statfs section of the reply
- -- @return pos number containing the offset after decoding
- -- @return statfs table with the following fields: transfer_size, block_size,
- -- total_blocks, free_blocks and available_blocks
- --
- StatFsDecode = function( self, data, pos )
- local catch = function() self.socket:close() end
- local try = nmap.new_try(catch)
- local statfs = {}
- local NFS_OK, NSFERR_ACCESS = 0, 13
-
- data = try( self.comm:GetAdditionalBytes( data, pos, 4 ) )
- pos, statfs.status = bin.unpack(">I", data, pos)
-
- if statfs.status ~= NFS_OK then
- if statfs.status == NSFERR_ACCESS then
- stdnse.print_debug("STATFS query received NSFERR_ACCESS")
- end
+ elseif ( comm.version == 3 ) then
+ status, data = comm:GetAdditionalBytes( data, pos, 84 )
+ if (not(status)) then
+ stdnse.print_debug("GetAttrDecode: Failed to call GetAdditionalBytes")
return -1, nil
end
+ pos, attrib.type, attrib.mode, attrib.nlink, attrib.uid,
+ attrib.gid, attrib.size, attrib.used, attrib.rdev,
+ attrib.fsid, attrib.fileid, attrib.atime, attrib.mtime,
+ attrib.ctime = bin.unpack(">IIIIILLLLLLLL", data, pos)
- data = try( self.comm:GetAdditionalBytes( data, pos, 20 ) )
- pos, statfs.transfer_size, statfs.block_size,
- statfs.total_blocks, statfs.free_blocks,
- statfs.available_blocks = bin.unpack(">IIIII", data, pos )
- return pos, statfs
- end,
+ else
+ stdnse.print_debug("GetAttrDecode: Unsupported version")
+ return -1, nil
+ end
+
+ return pos, attrib
+ end,
+
+ --- Gets mount attributes (uid, gid, mode, etc ..) from a remote NFS share
+ --
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
+ -- @param file_handle string containing the filehandle to query
+ -- @return status true on success, false on failure
+ -- @return attribs table with the fields type, mode,
+ -- nlink, uid, gid, size,
+ -- blocksize, rdev, blocks, fsid,
+ -- fileid, atime, mtime and ctime
+ -- @return errormsg if status is false
+ GetAttr = function( self, comm, file_handle )
+ local data, packet, status, attribs, pos, header
+
+ data = bin.pack("A", file_handle)
+ packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].GETATTR, { type=RPC.AuthType.Null }, data )
+ if(not(comm:SendPacket(packet))) then
+ return false, "GetAttr: Failed to send data"
+ end
+
+ status, data = comm:ReceivePacket()
+ if ( not(status) ) then
+ return false, "GetAttr: Failed to read data from socket"
+ end
+
+ pos, header = comm:DecodeHeader( data, 1 )
+ if not header then
+ return false, "GetAttr: Failed to decode header"
+ end
+
+ pos, attribs = self:GetAttrDecode(comm, data, pos )
+ if not attribs then
+ return false, "GetAttr: Failed to decode attrib structure"
+ end
+
+ return true, attribs
+ end,
+
+ --- Attempts to decode the StatFS section of the reply
+ --
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
+ -- @param data string containing the full statfs reply
+ -- @param pos number pointing to the statfs section of the reply
+ -- @return pos number containing the offset after decoding
+ -- @return statfs table with the following fields: transfer_size, block_size,
+ -- total_blocks, free_blocks and available_blocks
+ StatFsDecode = function( self, comm, data, pos )
+ local status
+ local statfs = {}
+
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if (not(status)) then
+ stdnse.print_debug("StatFsDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
+ end
+ pos, statfs.status = bin.unpack(">I", data, pos)
+
+ if (statfs.status ~= NFS.StatCode[comm.version].NFS_OK) then
+ if (NFS.StatMsg[statfs.status]) then
+ stdnse.print_debug(string.format("STATFS query failed: %s", NFS.StatMsg[statfs.status]))
+ else
+ stdnse.print_debug(string.format("STATFS query failed: code %d", statfs.status))
+ end
+ return -1, nil
+ end
+
+ status, data = comm:GetAdditionalBytes( data, pos, 20 )
+ if (not(status)) then
+ stdnse.print_debug("StatFsDecode: Failed to call GetAdditionalBytes")
+ return -1, nil
+ end
+ pos, statfs.transfer_size, statfs.block_size,
+ statfs.total_blocks, statfs.free_blocks,
+ statfs.available_blocks = bin.unpack(">IIIII", data, pos )
+ return pos, statfs
+ end,
}
Helper = {
@@ -1046,32 +1282,29 @@ Helper = {
-- @return status true on success, false on failure
-- @return result table of string entries or error message on failure
ShowMounts = function( host, port )
-
- local data, prog_tbl = {}, {}
- local status, result, mounts, response
- local socket = nmap.new_socket()
- local mountd
- local ver
- local mnt = Mount:new()
+
+ local status, result, mounts
+ local mountd, mnt_comm
+ local mnt = Mount:new()
local portmap = Portmap:new()
- local portmap_table, proginfo
status, mountd = Helper.GetProgramInfo( host, port, "mountd")
-
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for mountd"
+ stdnse.print_debug("rpc.Helper.ShowMounts: GetProgramInfo failed")
+ return status, "rpc.Helper.ShowMounts: GetProgramInfo failed"
end
-
- status, result = mnt:Connect( host, mountd.port, mountd.version )
+
+ mnt_comm = Comm:new('mountd', mountd.version)
+ status, result = mnt_comm:Connect(host, mountd.port)
if ( not(status) ) then
- stdnse.print_debug(3, result)
+ stdnse.print_debug("rpc.Helper.ShowMounts: %s", result)
return false, result
end
-
- status, mounts = mnt:Export()
-
- mnt:Disconnect()
-
+ status, mounts = mnt:Export(mnt_comm)
+ mnt_comm:Disconnect()
+ if ( not(status) ) then
+ stdnse.print_debug("rpc.Helper.ShowMounts: %s", mounts)
+ end
return status, mounts
end,
@@ -1084,53 +1317,66 @@ Helper = {
-- @return statfs table with the fields transfer_size, block_size,
-- total_blocks, free_blocks and available_blocks
ExportStats = function( host, port, path )
-
local fhandle
local stats, status, result
+ local mnt_comm, nfs_comm
local mountd, nfsd = {}, {}
local mnt, nfs = Mount:new(), NFS:new()
-
+
status, mountd = Helper.GetProgramInfo( host, port, "mountd", 2)
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for mountd"
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", mountd)
+ return status, mountd
end
status, nfsd = Helper.GetProgramInfo( host, port, "nfs", 2)
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for nfsd"
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", nfsd)
+ return status, nfsd
+ end
+ mnt_comm = Comm:new('mountd', mountd.version)
+ nfs_comm = Comm:new('nfs', nfsd.version)
+
+ -- TODO: recheck the version mismatch when adding NFSv4
+ if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
+ stdnse.print_debug("rpc.Helper.ExportStats: versions mismatch, nfs v%d - mount v%d",
+ nfs_comm.version, mnt_comm.version)
+ return false, string.format("versions mismatch, nfs v%d - mount v%d", nfs_comm.version, mnt_comm.version)
+ end
+ status, result = mnt_comm:Connect(host, mountd.port)
+ if ( not(status) ) then
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", result)
+ return status, result
+ end
+ status, result = nfs_comm:Connect(host, nfsd.port)
+ if ( not(status) ) then
+ mnt_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", result)
+ return status, result
end
- status, result = mnt:Connect( host, mountd.port, mountd.version )
+ status, fhandle = mnt:Mount(mnt_comm, path)
if ( not(status) ) then
- return false, "Failed to connect to mountd program"
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", fhandle)
+ return status, fhandle
end
-
- status, result = nfs:Connect( host, nfsd.port, nfsd.version )
+ status, stats = nfs:StatFs(nfs_comm, fhandle)
if ( not(status) ) then
- mnt:Disconnect()
- return false, "Failed to connect to nfsd program"
- end
-
- status, fhandle = mnt:Mount( path )
- if ( not(status) ) then
- mnt:Disconnect()
- nfs:Disconnect()
- stdnse.print_debug("rpc.Helper.ExportStats: mount failed")
- return false, "Mount failed"
- end
-
- status, stats = nfs:StatFs( fhandle )
- if ( not(status) ) then
- mnt:Disconnect()
- nfs:Disconnect()
- return false, stats
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", stats)
+ return status, stats
end
- status, fhandle = mnt:Unmount( path )
-
- mnt:Disconnect()
- nfs:Disconnect()
-
+ status, fhandle = mnt:Unmount(mnt_comm, path)
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ if ( not(status) ) then
+ stdnse.print_debug("rpc.Helper.ExportStats: %s", fhandle)
+ return status, fhandle
+ end
return true, stats
end,
@@ -1142,58 +1388,71 @@ Helper = {
-- @return status true on success, false on failure
-- @return table of file table entries as described in decodeReadDir
Dir = function( host, port, path )
-
local fhandle
local dirs, status, result
local mountd, nfsd = {}, {}
+ local mnt_comm, nfs_comm
local mnt, nfs = Mount:new(), NFS:new()
status, mountd = Helper.GetProgramInfo( host, port, "mountd")
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for mountd"
+ stdnse.print_debug("rpc.Helper.Dir: %s", mountd)
+ return status, mountd
end
status, nfsd = Helper.GetProgramInfo( host, port, "nfs")
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for nfsd"
+ stdnse.print_debug("rpc.Helper.Dir: %s", nfsd)
+ return status, nfsd
end
- status, result = mnt:Connect( host, mountd.port, mountd.version )
+ mnt_comm = Comm:new('mountd', mountd.version)
+ nfs_comm = Comm:new('nfs', nfsd.version)
+
+ -- TODO: recheck the version mismatch when adding NFSv4
+ if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
+ stdnse.print_debug("rpc.Helper.Dir: versions mismatch, nfs v%d - mount v%d",
+ nfs_comm.version, mnt_comm.version)
+ return false, string.format("versions mismatch, nfs v%d - mount v%d",
+ nfs_comm.version, mnt_comm.version)
+ end
+ status, result = mnt_comm:Connect(host, mountd.port)
if ( not(status) ) then
- return false, "Failed to connect to mountd program"
+ stdnse.print_debug("rpc.Helper.Dir: %s", result)
+ return status, result
end
- status, result = nfs:Connect( host, nfsd.port, nfsd.version )
+ status, result = nfs_comm:Connect(host, nfsd.port)
if ( not(status) ) then
- mnt:Disconnect()
- return false, "Failed to connect to nfsd program"
+ mnt_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.Dir: %s", result)
+ return status, result
end
- status, fhandle = mnt:Mount( path )
+ status, fhandle = mnt:Mount(mnt_comm, path )
if ( not(status) ) then
- mnt:Disconnect()
- nfs:Disconnect()
- return false, "rpc.Helper.Dir: mount failed"
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.Dir: %s", fhandle)
+ return status, fhandle
end
- status, dirs = nfs:ReadDir( fhandle )
+ status, dirs = nfs:ReadDir(nfs_comm, fhandle )
if ( not(status) ) then
- mnt:Disconnect()
- nfs:Disconnect()
- return false, "rpc.Helper.Dir: statfs failed"
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.Dir: %s", dirs)
+ return status, dirs
end
- status, fhandle = mnt:Unmount( path )
-
- mnt:Disconnect()
- nfs:Disconnect()
-
+ status, fhandle = mnt:Unmount(mnt_comm, path)
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
if ( not(status) ) then
- return false, "rpc.Helper.Dir: mount failed"
+ stdnse.print_debug("rpc.Helper.Dir: %s", fhandle)
+ return status, fhandle
end
-
return true, dirs
-
end,
--- Retrieves NFS Attributes
@@ -1207,51 +1466,69 @@ Helper = {
GetAttributes = function( host, port, path )
local fhandle
local attribs, status, result
+ local mnt_comm, nfs_comm
local mountd, nfsd = {}, {}
local mnt, nfs = Mount:new(), NFS:new()
status, mountd = Helper.GetProgramInfo( host, port, "mountd")
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for mountd"
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", mountd)
+ return status, mountd
end
status, nfsd = Helper.GetProgramInfo( host, port, "nfs")
if ( not(status) ) then
- return false, "Failed to retrieve rpc information for nfsd"
- end
-
- status, result = mnt:Connect( host, mountd.port, mountd.version )
- if ( not(status) ) then
- return false, "Failed to connect to mountd program"
- end
-
- status, result = nfs:Connect( host, nfsd.port, nfsd.version )
- if ( not(status) ) then
- mnt:Disconnect()
- return false, "Failed to connect to nfsd program"
- end
-
- status, fhandle = mnt:Mount( path )
- if ( not(status) ) then
- mnt:Disconnect()
- nfs:Disconnect()
- return false, "rpc.Helper.GetAttributes: mount failed"
- end
-
- status, attribs = nfs:GetAttr( fhandle )
- if ( not(status) ) then
- mnt:Disconnect()
- nfs:Disconnect()
- return false, "rpc.Helper.GetAttributes: GetAttr failed"
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", nfsd)
+ return status, nfsd
end
- status, fhandle = mnt:Unmount( path )
-
- mnt:Disconnect()
- nfs:Disconnect()
-
+ mnt_comm, result = Comm:new('mountd', mountd.version)
+ nfs_comm, result = Comm:new('nfs', nfsd.version)
+
+ -- TODO: recheck the version mismatch when adding NFSv4
+ if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
+ stdnse.print_debug("rpc.Helper.GetAttributes: versions mismatch, nfs v%d - mount v%d",
+ nfs_comm.version, mnt_comm.version)
+ return false, string.format("versions mismatch, nfs v%d - mount v%d",
+ nfs_comm.version, mnt_comm.version)
+ end
+
+ status, result = mnt_comm:Connect(host, mountd.port)
if ( not(status) ) then
- return false, "rpc.Helper.ExportStats: mount failed"
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", result)
+ return status, result
+ end
+
+ status, result = nfs_comm:Connect(host, nfsd.port)
+ if ( not(status) ) then
+ mnt_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", result)
+ return status, result
+ end
+
+ status, fhandle = mnt:Mount(mnt_comm, path)
+ if ( not(status) ) then
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", fhandle)
+ return status, fhandle
+ end
+
+ status, attribs = nfs:GetAttr(nfs_comm, fhandle)
+ if ( not(status) ) then
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", attribs)
+ return status, attribs
+ end
+
+ status, fhandle = mnt:Unmount(mnt_comm, path)
+
+ mnt_comm:Disconnect()
+ nfs_comm:Disconnect()
+ if ( not(status) ) then
+ stdnse.print_debug("rpc.Helper.GetAttributes: %s", fhandle)
+ return status, fhandle
end
return true, attribs
@@ -1265,20 +1542,24 @@ Helper = {
-- @return table containing the portmapper information as returned by
-- Portmap.Dump
RpcInfo = function( host, port )
+ local status, result
local portmap = Portmap:new()
- local status = Portmap:Connect(host, port)
- local result
+ local comm = Comm:new('rpcbind', 2)
- if ( not(status) ) then
- return
+ status, result = comm:Connect(host, port)
+ if (not(status)) then
+ stdnse.print_debug("rpc.Helper.RpcInfo: %s", result)
+ return status, result
+ end
+ status, result = portmap:Dump(comm)
+ comm:Disconnect()
+ if (not(status)) then
+ stdnse.print_debug("rpc.Helper.RpcInfo: %s", result)
end
-
- status, result = portmap:Dump()
- portmap:Disconnect()
return status, result
end,
-
+
--- Queries the portmapper for a port for the specified RPC program
--
-- @param host table
@@ -1289,17 +1570,22 @@ Helper = {
-- @return table containing the portmapper information as returned by
-- Portmap.Dump
GetPortForProgram = function( host, port, program_id, protocol )
+ local status, result
local portmap = Portmap:new()
- local status = Portmap:Connect(host, port)
- local result
-
- if ( not(status) ) then
- return
+ local comm = Comm:new('rpcbind', 2)
+
+ status, result = comm:Connect(host, port)
+ if (not(status)) then
+ stdnse.print_debug("rpc.Helper.GetPortForProgram: %s", result)
+ return status, result
end
- status, result = portmap:GetPort( program_id, protocol, 1 )
- portmap:Disconnect()
-
+ status, result = portmap:GetPort(comm, program_id, protocol, 1 )
+ comm:Disconnect()
+ if (not(status)) then
+ stdnse.print_debug("rpc.Helper.GetPortForProgram: %s", result)
+ end
+
return status, result
end,
@@ -1313,23 +1599,11 @@ Helper = {
-- @return info table containing port, port.number
-- port.protocol and version
GetProgramInfo = function( host, port, program, max_version )
-
- local status, response
- local portmap_table, info
- local portmap = Portmap:new()
+ local info
- status, response = portmap:Connect( host, port )
+ local status, portmap_table = Helper.RpcInfo(host, port)
if ( not(status) ) then
- return false, "rpc.Helper.ShowMounts: Failed to connect to portmap"
- end
- status, portmap_table = portmap:Dump()
- if ( not(status) ) then
- portmap:Disconnect()
- return false, "rpc.Helper.ShowMounts: Failed to GetProgramVersions"
- end
- status = portmap:Disconnect()
- if ( not(status) ) then
- return false, "rpc.Helper.ShowMounts: Failed to disconnect from portmap"
+ return status, portmap_table
end
-- assume failure
@@ -1344,12 +1618,12 @@ Helper = {
info.port.number = tmp[p].port
info.port.protocol = p
-- choose the highest version available
- if ( not(Version[program]) ) then
+ if ( not(RPC_version[program]) ) then
info.version = tmp[p].version[#tmp[p].version]
status = true
else
for i=#tmp[p].version, 1, -1 do
- if ( Version[program].max >= tmp[p].version[i] ) then
+ if ( RPC_version[program].max >= tmp[p].version[i] ) then
if ( not(max_version) ) then
info.version = tmp[p].version[i]
status = true
@@ -1397,6 +1671,27 @@ RPC =
},
+ State =
+ {
+ Accepted = 0,
+ Denied = 1,
+ },
+
+ AcceptState =
+ {
+ SUCCESS = 0,
+ PROG_UNAVAIL = 1,
+ PROG_MISMATCH = 2,
+ PROC_UNAVAIL = 3,
+ GARBAGE_ARGS = 4,
+ },
+
+ RejectState =
+ {
+ RPC_MISMATCH = 0,
+ AUTH_ERROR = 1,
+ }
+
}
--- Portmap class
@@ -1412,42 +1707,12 @@ Portmap =
setmetatable(o, self)
self.__index = self
return o
- end,
-
- --- Connects to the Portmapper
- --
- -- @param host table
- -- @param port table
- -- @param version number containing the program version to use
- -- @return status boolean true on success, false on failure
- -- @return result string containing error message (if status is false)
- Connect = function( self, host, port, version )
- local socket = nmap.new_socket()
- local status, result = socket:connect(host.ip, port.number, port.protocol)
-
- if ( status ) then
- self.socket = socket
- self.version = version or 2
- self.protocol = port.protocol
- end
-
- return status, result
end,
-
- --- Disconnects from the portmapper program
- --
- -- @return status boolean true on success, false on failure
- -- @return result string containing error message (if status is false)
- Disconnect = function( self )
- local status, result = self.socket:close()
- if ( status ) then
- self.socket = nil
- end
- return status, result
- end,
-
+
--- Dumps a list of RCP programs from the portmapper
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @return status boolean true on success, false on failure
-- @return result table containing RPC program information or error message
-- on failure. The table has the following format:
@@ -1461,20 +1726,16 @@ Portmap =
-- o program_id is the number associated with the program
-- o protocol is either "tcp" or "udp"
--
- Dump = function( self )
+ Dump = function(self, comm)
local status, data, packet, response, pos, header
-
- local prog_id = Util.ProgNameToNumber("rpcbind")
- local prog_proc = RPC.Procedure[self.version].DUMP
- local comm
-
if ( self.program_table ) then
return true, self.program_table
end
- comm = Comm:new( { socket=self.socket, proto=self.protocol } )
- packet = comm:EncodePacket( nil, { id=prog_id, version=self.version, proc=prog_proc }, { type=RPC.AuthType.Null }, data )
- status, response = comm:SendPacket( packet )
+ packet = comm:EncodePacket( nil, RPC.Procedure[comm.version].DUMP, { type=RPC.AuthType.Null }, data )
+ if (not(comm:SendPacket(packet))) then
+ return false, "Portmap.Dump: Failed to send data"
+ end
status, data = comm:ReceivePacket()
if ( not(status) ) then
return false, "Portmap.Dump: Failed to read data from socket"
@@ -1482,10 +1743,10 @@ Portmap =
pos, header = comm:DecodeHeader( data, 1 )
if ( not(header) ) then
- return false, "Failed to decode RPC header"
+ return false, "Portmap.Dump: Failed to decode RPC header"
end
if header.accept_state ~= 0 then
- return false, string.format("RPC Accept State was not Successful")
+ return false, "Portmap.Dump: RPC Accept State was not Successful"
end
self.program_table = {}
@@ -1494,15 +1755,16 @@ Portmap =
local vfollows
local program, version, protocol, port
- status, data = comm:GetAdditionalBytes( data, pos, 4 )
- pos, vfollows = bin.unpack( ">I", data, pos )
-
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if ( not(status) ) then
+ return false, "Portmap.Dump: Failed to call GetAdditionalBytes"
+ end
+ pos, vfollows = bin.unpack( ">I", data, pos )
if ( vfollows == 0 ) then
break
end
pos, program, version, protocol, port = bin.unpack(">IIII", data, pos)
-
if ( protocol == Portmap.PROTOCOLS.tcp ) then
protocol = "tcp"
elseif ( protocol == Portmap.PROTOCOLS.udp ) then
@@ -1526,51 +1788,53 @@ Portmap =
--- Queries the portmapper for the port of the selected program,
-- protocol and version
--
+ -- @param Comm object handles rpc program information and
+ -- low-level packet manipulation
-- @param program string name of the program
-- @param protocol string containing either "tcp" or "udp"
-- @param version number containing the version of the queried program
-- @return number containing the port number
- GetPort = function( self, program, protocol, version )
+ GetPort = function( self, comm, program, protocol, version )
local status, data, response, header, pos, packet
local xid
- local prog_id = Util.ProgNameToNumber("rpcbind") -- RPC Portmap
- local prog_proc = RPC.Procedure[self.version].GETPORT
- local comm
if ( not( Portmap.PROTOCOLS[protocol] ) ) then
- return false, ("Protocol %s not supported"):format(protocol)
+ return false, ("Portmap.GetPort: Protocol %s not supported"):format(protocol)
end
- if ( Util.ProgNameToNumber( program ) == nil ) then
- return false, ("Unknown program name: %s"):format(program)
+ if ( Util.ProgNameToNumber(program) == nil ) then
+ return false, ("Portmap.GetPort: Unknown program name: %s"):format(program)
end
- comm = Comm:new( { socket=self.socket, proto=self.protocol } )
data = bin.pack( ">I>I>I>I", Util.ProgNameToNumber(program), version, Portmap.PROTOCOLS[protocol], 0 )
- packet = comm:EncodePacket( xid, { id=prog_id, version=self.version, proc=prog_proc }, { type=RPC.AuthType.Null }, data )
+ packet = comm:EncodePacket( xid, RPC.Procedure[comm.version].GETPORT, { type=RPC.AuthType.Null }, data )
- status = comm:SendPacket(packet)
+ if (not(comm:SendPacket(packet))) then
+ return false, "Portmap.GetPort: Failed to send data"
+ end
+
data = ""
-
status, data = comm:ReceivePacket()
if ( not(status) ) then
- return false, "GetPort: Failed to read data from socket"
+ return false, "Portmap.GetPort: Failed to read data from socket"
end
pos, header = comm:DecodeHeader( data, 1 )
if ( not(header) ) then
- return false, "Failed to decode RPC header"
+ return false, "Portmap.GetPort: Failed to decode RPC header"
end
if header.accept_state ~= 0 then
- return false, string.format("RPC Accept State was not Successful")
+ return false, "Portmap.GetPort: RPC Accept State was not Successful"
end
- status, data = comm:GetAdditionalBytes( data, pos, 4 )
- return true, select(2, bin.unpack(">I", data, pos ) )
-
+ status, data = comm:GetAdditionalBytes( data, pos, 4 )
+ if ( not(status) ) then
+ return false, "Portmap.GetPort: Failed to call GetAdditionalBytes"
+ end
+ return true, select(2, bin.unpack(">I", data, pos ) )
end,
-
+
}
--- Static class containing mostly conversion functions
diff --git a/scripts/nfs-acls.nse b/scripts/nfs-acls.nse
index be8cd1938..486544bc5 100644
--- a/scripts/nfs-acls.nse
+++ b/scripts/nfs-acls.nse
@@ -42,7 +42,7 @@ action = function(host, port)
status, mounts = rpc.Helper.ShowMounts( host, port )
if ( not(status) or mounts == nil ) then
- return " \n\n Failed to list mount points"
+ return stdnse.format_output(false, mounts)
end
for _, mount in ipairs( mounts ) do
@@ -54,7 +54,7 @@ action = function(host, port)
if ( status ) then
table.insert(item, ("uid: %d; gid: %d; mode: %s (%d)"):format(attribs.uid, attribs.gid, rpc.Util.ToAclText( attribs.mode ), rpc.Util.ToAclMode( attribs.mode )) )
else
- table.insert(item, "ERROR: Mount failed")
+ table.insert(item, string.format("ERROR: %s", attribs))
end
table.insert(result, item)
diff --git a/scripts/nfs-dirlist.nse b/scripts/nfs-dirlist.nse
index 6575c1409..ee5577537 100644
--- a/scripts/nfs-dirlist.nse
+++ b/scripts/nfs-dirlist.nse
@@ -47,7 +47,7 @@ action = function(host, port)
status, mounts = rpc.Helper.ShowMounts( host, port )
if ( not(status) ) then
- return " \n\n Failed to list mount points"
+ return stdnse.format_output(false, mounts)
end
for _, v in ipairs( mounts ) do
@@ -72,7 +72,7 @@ action = function(host, port)
table.sort(files)
if hasmore then
- files.name = v.name .. string.format(" (Output limited to %d files)", max_files )
+ files.name = v.name .. string.format(" (Output limited to %d files, see nfs-dirlist.maxfiles)", max_files )
else
files.name = v.name
end
@@ -80,7 +80,7 @@ action = function(host, port)
table.insert( result, files )
else
files.name = v.name
- table.insert(files, "ERROR: Mount failed")
+ table.insert(files, string.format("ERROR: %s",dirlist))
table.insert( result, files )
end
diff --git a/scripts/nfs-showmount.nse b/scripts/nfs-showmount.nse
index 666c8ede7..8f1956ef0 100644
--- a/scripts/nfs-showmount.nse
+++ b/scripts/nfs-showmount.nse
@@ -39,7 +39,7 @@ action = function(host, port)
status, mounts = rpc.Helper.ShowMounts( host, port )
if not status or mounts == nil then
- return " \n\n Failed to list mount points"
+ return stdnse.format_output(false, mounts)
end
for _, v in ipairs( mounts ) do
diff --git a/scripts/nfs-statfs.nse b/scripts/nfs-statfs.nse
index 1ac03520c..73bd9d247 100644
--- a/scripts/nfs-statfs.nse
+++ b/scripts/nfs-statfs.nse
@@ -40,26 +40,22 @@ action = function(host, port)
local status, mounts = rpc.Helper.ShowMounts( host, port )
if ( not(status) ) then
- return " \n\n Failed to list mount points"
+ return stdnse.format_output(false, mounts)
end
for _, v in ipairs( mounts ) do
local entry = {}
local status, stats = rpc.Helper.ExportStats(host, port, v.name)
- if ( not(status) and stats:match("Version %d not supported") ) then
- return " \n\n " .. stats
- end
-
entry.name = v.name
-
- if status and stats then
+
+ if (not(status)) then
+ table.insert(entry, string.format("ERROR: %s", stats))
+ else
table.insert( entry, string.format("Block size: %d", stats.block_size) )
table.insert( entry, string.format("Total blocks: %d", stats.total_blocks) )
table.insert( entry, string.format("Free blocks: %d", stats.free_blocks) )
table.insert( entry, string.format("Available blocks: %d", stats.available_blocks) )
- else
- table.insert( entry, "ERROR: Mount failed")
end
table.insert( result, entry )
end
diff --git a/scripts/rpcinfo.nse b/scripts/rpcinfo.nse
index 3441be042..335802e12 100644
--- a/scripts/rpcinfo.nse
+++ b/scripts/rpcinfo.nse
@@ -31,7 +31,7 @@ action = function(host, port)
local status, rpcinfo = rpc.Helper.RpcInfo( host, port )
if ( not(status) ) then
- return
+ return stdnse.format_output(false, rpcinfo)
end
for progid, v in pairs(rpcinfo) do