mirror of
https://github.com/nmap/nmap.git
synced 2025-12-16 04:39:03 +00:00
Merge r18534:r18591 from nmap-exp/djalal/nse-nfs/
This commit is contained in:
119
nselib/rpc.lua
119
nselib/rpc.lua
@@ -1795,7 +1795,7 @@ NFS = {
|
||||
FsStat = function(self, comm, file_handle)
|
||||
local status, packet
|
||||
local pos, data = 1, ""
|
||||
local reponse = {}
|
||||
local header, response = {}, {}
|
||||
|
||||
if (comm.version < 3) then
|
||||
return false, string.format("NFS version: %d does not support FSSTAT",
|
||||
@@ -1867,7 +1867,7 @@ NFS = {
|
||||
pos, fsinfo.rtmax, fsinfo.rtpref, fsinfo.rtmult,
|
||||
fsinfo.wtmax, fsinfo.wtpref, fsinfo.wtmult,
|
||||
fsinfo.dtpref = Util.unmarshall_uint32(data, pos, 7)
|
||||
pos, fsinfo.maxfilesize = Util.unmarshall_size3(data, pos)
|
||||
pos, fsinfo.maxfilesize = Util.unmarshall_nfssize3(data, pos)
|
||||
pos, fsinfo.time_delta = Util.unmarshall_nfstime(data, pos)
|
||||
pos, fsinfo.properties = Util.unmarshall_uint32(data, pos)
|
||||
|
||||
@@ -1877,7 +1877,7 @@ NFS = {
|
||||
FsInfo = function(self, comm, file_handle)
|
||||
local status, packet
|
||||
local pos, data = 1, ""
|
||||
local reponse = {}
|
||||
local header, response = {}
|
||||
|
||||
if (comm.version < 3) then
|
||||
return false, string.format("NFS version: %d does not support FSINFO",
|
||||
@@ -1913,7 +1913,7 @@ NFS = {
|
||||
return true, response
|
||||
end,
|
||||
|
||||
PathConfDecode = function(self, comm, file_handle)
|
||||
PathConfDecode = function(self, comm, data, pos)
|
||||
local pconf, status, value_follows = {}
|
||||
|
||||
status, data = comm:GetAdditionalBytes(data, pos, 4)
|
||||
@@ -1956,7 +1956,7 @@ NFS = {
|
||||
PathConf = function(self, comm, file_handle)
|
||||
local status, packet
|
||||
local pos, data = 1, ""
|
||||
local reponse = {}
|
||||
local header, response = {}
|
||||
|
||||
if (comm.version < 3) then
|
||||
return false, string.format("NFS version: %d does not support PATHCONF",
|
||||
@@ -3071,12 +3071,12 @@ Util =
|
||||
-- readable format
|
||||
SizeToHuman = function(size, blocksize)
|
||||
local bs, idx = 1024, 1
|
||||
local unit = { "B", "K", "M", "G" }
|
||||
local unit = { "B", "K", "M", "G" , "T"}
|
||||
if blocksize and blocksize == 1000 then
|
||||
bs = blocksize
|
||||
end
|
||||
for i=1, #unit do
|
||||
if (size > bs) then
|
||||
if (size > bs and idx < #unit) then
|
||||
size = size / bs
|
||||
idx = idx + 1
|
||||
end
|
||||
@@ -3126,6 +3126,111 @@ Util =
|
||||
return ret
|
||||
end,
|
||||
|
||||
--- Return the pathconf filesystem table
|
||||
--
|
||||
-- @param pconf table returned by the NFSv3 PATHCONF call
|
||||
-- @param nfsversion the version of the remote NFS server
|
||||
-- @return fs table that contains the remote filesystem
|
||||
-- pathconf information.
|
||||
calc_pathconf_table = function(pconf, nfsversion)
|
||||
local fs = {}
|
||||
if nfsversion ~= 3 then
|
||||
return nil, "ERROR: unsupported NFS version."
|
||||
end
|
||||
|
||||
fs.linkmax = pconf.linkmax
|
||||
fs.name_max = pconf.name_max
|
||||
|
||||
if pconf.chown_restricted then
|
||||
fs.chown_restricted = "True"
|
||||
else
|
||||
fs.chown_restricted = "False"
|
||||
end
|
||||
|
||||
return fs, nil
|
||||
end,
|
||||
|
||||
--- Calculate and return the fsinfo filesystem table
|
||||
--
|
||||
-- @param fsinfo table returned by the NFSv3 FSINFO call
|
||||
-- @param nfsversion the version of the remote NFS server
|
||||
-- @param human if set show the size in the human
|
||||
-- readable format.
|
||||
-- @return fs table that contains the remote filesystem
|
||||
-- information.
|
||||
calc_fsinfo_table = function(fsinfo, nfsversion, human)
|
||||
local fs = {}
|
||||
local nfsobj = NFS:new()
|
||||
if nfsversion ~= 3 then
|
||||
return nil, "ERROR: unsupported NFS version."
|
||||
end
|
||||
|
||||
fs.maxfilesize = Util.SizeToHuman(fsinfo.maxfilesize)
|
||||
|
||||
if nfsobj:FSinfoLink(fsinfo.properties, nfsversion) ~= 0 then
|
||||
fs.link = "True"
|
||||
else
|
||||
fs.link = "False"
|
||||
end
|
||||
|
||||
if nfsobj:FSinfoSymlink(fsinfo.properties, nfsversion) ~= 0 then
|
||||
fs.symlink = "True"
|
||||
else
|
||||
fs.symlink = "False"
|
||||
end
|
||||
|
||||
return fs, nil
|
||||
end,
|
||||
|
||||
--- Calculate and return the fsstat filesystem table
|
||||
--
|
||||
-- @param stats table returned by the NFSv3 FSSTAT or
|
||||
-- NFSv2 STATFS calls
|
||||
-- @param nfsversion the version of the remote NFS server
|
||||
-- @param human if set show the size in the human
|
||||
-- readable format.
|
||||
-- @return df table that contains the remote filesystem
|
||||
-- attributes.
|
||||
calc_fsstat_table = function(stats, nfsversion, human)
|
||||
local df, base = {}, 1024
|
||||
local size, free, total, avail, used, use
|
||||
if (nfsversion == 3) then
|
||||
free = stats.fbytes
|
||||
size = stats.tbytes
|
||||
avail = stats.abytes
|
||||
elseif (nfsversion == 2) then
|
||||
df.bsize = stats.block_size
|
||||
free = stats.free_blocks * df.bsize
|
||||
size = stats.total_blocks * df.bsize
|
||||
avail = stats.available_blocks * df.bsize
|
||||
else
|
||||
return nil, "ERROR: unsupported NFS version."
|
||||
end
|
||||
|
||||
if (human) then
|
||||
if (df.bsize) then
|
||||
df.bsize = Util.SizeToHuman(df.bsize)
|
||||
end
|
||||
df.size = Util.SizeToHuman(size)
|
||||
df.available = Util.SizeToHuman(avail)
|
||||
used = size - free
|
||||
avail = avail
|
||||
df.used = Util.SizeToHuman(used)
|
||||
total = used + avail
|
||||
else
|
||||
free = free / base
|
||||
df.size = size / base
|
||||
df.available = avail / base
|
||||
used = df.size - free
|
||||
df.used = used
|
||||
total = df.used + df.available
|
||||
end
|
||||
|
||||
use = math.ceil(used * 100 / total)
|
||||
df.use = string.format("%.0f%%", use)
|
||||
return df, nil
|
||||
end,
|
||||
|
||||
--- Converts a RPC program name to it's equivalent number
|
||||
--
|
||||
-- @param prog_name string containing the name of the RPC program
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
description = [[
|
||||
Retrieves disk space statistics from the remote NFS share
|
||||
Retrieves disk space statistics and information from the remote NFS
|
||||
share. This script will try to emulate the behaviour of the "df" tool.
|
||||
|
||||
The script will provide pathconf information of the remote NFS if
|
||||
the version used is NFSv3.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @output
|
||||
-- PORT STATE SERVICE
|
||||
-- | nfs-statfs:
|
||||
-- | /home/storage/backup
|
||||
-- | Block size: 512
|
||||
-- | Total blocks: 1901338728
|
||||
-- | Free blocks: 729769328
|
||||
-- | Available blocks: 633186880
|
||||
-- | /home
|
||||
-- | Block size: 512
|
||||
-- | Total blocks: 1901338728
|
||||
-- | Free blocks: 729769328
|
||||
-- |_ Available blocks: 633186880
|
||||
-- |
|
||||
-- | Filesystem 1K-blocks Used Available Use% Blocksize
|
||||
-- | /mnt/nfs/files 5542276 2732012 2528728 52% 4096
|
||||
-- |_ /mnt/nfs/opensource 5534416 620640 4632644 12% 4096
|
||||
--
|
||||
-- @args nfs-statfs.human If set to '1' or 'true' shows the filesystem
|
||||
-- size in the human readable format.
|
||||
--
|
||||
|
||||
-- Version 0.3
|
||||
@@ -23,43 +24,185 @@ Retrieves disk space statistics from the remote NFS share
|
||||
-- Created 01/25/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 02/22/2010 - v0.2 - adapted to support new RPC library
|
||||
-- Revised 03/13/2010 - v0.3 - converted host to port rule
|
||||
-- Revised 06/28/2010 - v0.4 - added NFSv3 support and doc
|
||||
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
author = "Patrik Karlsson, Djalal Harouni"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require("shortport")
|
||||
require("rpc")
|
||||
require("tab")
|
||||
|
||||
portrule = shortport.port_or_service(111, "rpcbind", {"tcp", "udp"} )
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local result, entry = {}, {}
|
||||
local status, mounts = rpc.Helper.ShowMounts( host, port )
|
||||
|
||||
if ( not(status) ) then
|
||||
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)
|
||||
|
||||
entry.name = v.name
|
||||
|
||||
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) )
|
||||
end
|
||||
table.insert( result, entry )
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, result )
|
||||
|
||||
local function table_fsstat(nfs, mount, stats)
|
||||
local fs, err = rpc.Util.calc_fsstat_table(stats, nfs.version, nfs.human)
|
||||
if fs == nil then
|
||||
return false, err
|
||||
end
|
||||
fs.filesystem = string.format("%s", mount)
|
||||
return true, fs
|
||||
end
|
||||
|
||||
local function table_fsinfo(nfs, fsinfo)
|
||||
local ret = {}
|
||||
local fs, err = rpc.Util.calc_fsinfo_table(fsinfo, nfs.version, nfs.human)
|
||||
if fs == nil then
|
||||
return false, err
|
||||
end
|
||||
|
||||
ret.maxfilesize = fs.maxfilesize
|
||||
return true, ret
|
||||
end
|
||||
|
||||
local function table_pathconf(nfs, pconf)
|
||||
local ret = {}
|
||||
local fs, err = rpc.Util.calc_pathconf_table(pconf, nfs.version)
|
||||
if fs == nil then
|
||||
return false, err
|
||||
end
|
||||
|
||||
ret.linkmax = fs.linkmax
|
||||
return true, ret
|
||||
end
|
||||
|
||||
local function report(nfs, tables)
|
||||
local outtab, tab_size, tab_avail
|
||||
local tab_filesys, tab_used, tab_use,
|
||||
tab_bs, tab_maxfs, tab_linkmax = " Filesystem",
|
||||
"Used", "Use%", "Blocksize", "Maxfilesize", "Maxlink"
|
||||
|
||||
if nfs.human then
|
||||
tab_size = "Size"
|
||||
tab_avail = "Avail"
|
||||
else
|
||||
tab_size = "1K-blocks"
|
||||
tab_avail = "Available"
|
||||
end
|
||||
|
||||
if nfs.version == 2 then
|
||||
outtab = tab.new(6)
|
||||
tab.nextrow(outtab)
|
||||
tab.addrow(outtab, tab_filesys, tab_size, tab_used,
|
||||
tab_avail, tab_use, tab_bs)
|
||||
for _, t in ipairs(tables) do
|
||||
tab.nextrow(outtab)
|
||||
tab.addrow(outtab, (" %s"):format(t.filesystem), t.size,
|
||||
t.used, t.available, t.use, t.bsize)
|
||||
end
|
||||
elseif nfs.version == 3 then
|
||||
outtab = tab.new(7)
|
||||
tab.nextrow(outtab)
|
||||
tab.addrow(outtab, tab_filesys, tab_size, tab_used,
|
||||
tab_avail, tab_use, tab_maxfs, tab_linkmax)
|
||||
for _, t in ipairs(tables) do
|
||||
tab.nextrow(outtab)
|
||||
tab.addrow(outtab, (" %s"):format(t.filesystem), t.size,
|
||||
t.used, t.available, t.use, t.maxfilesize,
|
||||
t.linkmax)
|
||||
end
|
||||
end
|
||||
|
||||
return tab.dump(outtab)
|
||||
end
|
||||
|
||||
local function nfs_filesystem_info(nfs, mount, filesystem)
|
||||
local results, res, status = {}, {}
|
||||
local nfsobj = rpc.NFS:new()
|
||||
local mnt_comm, nfs_comm, fhandle
|
||||
|
||||
mnt_comm, fhandle = rpc.Helper.MountPath(nfs.host, nfs.port, mount)
|
||||
if mnt_comm == nil then
|
||||
return false, fhandle
|
||||
end
|
||||
|
||||
local nfs_comm, status = rpc.Helper.NfsOpen(nfs.host, nfs.port)
|
||||
if nfs_comm == nil then
|
||||
rpc.Helper.UnmountPath(mnt_comm, mount)
|
||||
return false, status
|
||||
end
|
||||
|
||||
nfs.version = nfs_comm.version
|
||||
|
||||
-- use simple check since NFSv1 is not used anymore.
|
||||
if (mnt_comm.version ~= nfs_comm.version) then
|
||||
rpc.Helper.UnmountPath(mnt_comm, mount)
|
||||
return false, string.format("versions mismatch, nfs v%d - mount v%d",
|
||||
nfs_comm.version, mnt_comm.version)
|
||||
end
|
||||
|
||||
if nfs_comm.version < 3 then
|
||||
status, res = nfsobj:StatFs(nfs_comm, fhandle)
|
||||
elseif nfs_comm.version == 3 then
|
||||
status, res = nfsobj:FsStat(nfs_comm, fhandle)
|
||||
end
|
||||
|
||||
if status then
|
||||
status, res = table_fsstat(nfs, mount, res)
|
||||
if status then
|
||||
for k, v in pairs(res) do
|
||||
results[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if nfs_comm.version == 3 then
|
||||
status, res = nfsobj:FsInfo(nfs_comm, fhandle)
|
||||
if status then
|
||||
status, res = table_fsinfo(nfs, res)
|
||||
if status then
|
||||
for k, v in pairs(res) do
|
||||
results[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
status, res = nfsobj:PathConf(nfs_comm, fhandle)
|
||||
if status then
|
||||
status, res = table_pathconf(nfs, res)
|
||||
if status then
|
||||
for k, v in pairs(res) do
|
||||
results[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
rpc.Helper.NfsClose(nfs_comm)
|
||||
rpc.Helper.UnmountPath(mnt_comm, mount)
|
||||
if (not(status)) then
|
||||
return status, res
|
||||
end
|
||||
|
||||
table.insert(filesystem, results)
|
||||
return true, nil
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local o, fs_info, mounts, status = {}, {}, {}, {}
|
||||
local nfs_info =
|
||||
{
|
||||
host = host,
|
||||
port = port,
|
||||
human = nmap.registry.args['nfs-statfs.human'] or nil,
|
||||
}
|
||||
|
||||
status, mounts = rpc.Helper.ShowMounts( host, port )
|
||||
if (not(status)) then
|
||||
return stdnse.format_output(false, mounts)
|
||||
end
|
||||
|
||||
for _, v in ipairs(mounts) do
|
||||
local err
|
||||
status, err = nfs_filesystem_info(nfs_info, v.name, fs_info)
|
||||
if (not(status)) then
|
||||
return stdnse.format_output(false, err)
|
||||
end
|
||||
end
|
||||
table.insert(o, report(nfs_info, fs_info))
|
||||
|
||||
return stdnse.format_output(true, o)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user