PageRenderTime 75ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/nselib/rpc.lua

https://gitlab.com/g10h4ck/nmap-gsoc2015
Lua | 3435 lines | 2596 code | 352 blank | 487 comment | 295 complexity | 5fd291264c62a4720f829206fe648611 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
  1. ---
  2. -- RPC Library supporting a very limited subset of operations.
  3. --
  4. -- The library works over both the UDP and TCP protocols. A subset of nfs and
  5. -- mountd procedures are supported. The nfs and mountd programs support
  6. -- versions 1 through 3. Authentication is supported using the NULL RPC
  7. -- Authentication protocol
  8. --
  9. -- The library contains the following classes:
  10. -- * <code>Comm </code>
  11. -- ** Handles network connections.
  12. -- ** Handles low-level packet sending, receiving, decoding and encoding.
  13. -- ** Stores rpc programs info: socket, protocol, program name, id and version.
  14. -- ** Used by Mount, NFS, RPC and Portmap.
  15. -- * <code>Portmap</code>
  16. -- ** Contains RPC constants.
  17. -- ** Handles communication with the portmap RPC program.
  18. -- * <code>Mount</code>
  19. -- ** Handles communication with the mount RPC program.
  20. -- * <code>NFS</code>
  21. -- ** Handles communication with the nfs RPC program.
  22. -- * <code>Helper</code>
  23. -- ** Provides easy access to common RPC functions.
  24. -- ** Implemented as a static class where most functions accept host and port parameters.
  25. -- * <code>Util</code>
  26. -- ** Mostly static conversion routines.
  27. --
  28. -- The portmapper dynamically allocates TCP/UDP ports to RPC programs. So in
  29. -- in order to request a list of NFS shares from the server we need to:
  30. -- * Make sure that we can talk to the portmapper on port 111 TCP or UDP.
  31. -- * Query the portmapper for the ports allocated to the NFS program.
  32. -- * Query the NFS program for a list of shares on the ports returned by the portmap program.
  33. --
  34. -- The Helper class contains functions that facilitate access to common
  35. -- RPC program procedures through static class methods. Most functions accept
  36. -- host and port parameters. As the Helper functions query the portmapper to
  37. -- get the correct RPC program port, the port supplied to these functions
  38. -- should be the rpcbind port 111/tcp or 111/udp.
  39. --
  40. -- The following sample code illustrates how scripts can use the <code>Helper</code> class
  41. -- to interface the library:
  42. --
  43. -- <code>
  44. -- -- retrieve a list of NFS export
  45. -- status, mounts = rpc.Helper.ShowMounts( host, port )
  46. --
  47. -- -- iterate over every share
  48. -- for _, mount in ipairs( mounts ) do
  49. --
  50. -- -- get the NFS attributes for the share
  51. -- status, attribs = rpc.Helper.GetAttributes( host, port, mount.name )
  52. -- .... process NFS attributes here ....
  53. -- end
  54. -- </code>
  55. --
  56. -- RPC transaction IDs (XID) are not properly implemented as a random ID is
  57. -- generated for each client call. The library makes no attempt to verify
  58. -- whether the returned XID is valid or not.
  59. --
  60. -- Therefore TCP is the preferred method of communication and the library
  61. -- always attempts to connect to the TCP port of the RPC program first.
  62. -- This behaviour can be overridden by setting the rpc.protocol argument.
  63. -- The portmap service is always queried over the protocol specified in the
  64. -- port information used to call the Helper function from the script.
  65. --
  66. -- When multiple versions exists for a specific RPC program the library
  67. -- always attempts to connect using the highest available version.
  68. --
  69. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  70. --
  71. -- @author "Patrik Karlsson <patrik@cqure.net>"
  72. --
  73. -- @args nfs.version number If set overrides the detected version of nfs
  74. -- @args mount.version number If set overrides the detected version of mountd
  75. -- @args rpc.protocol table If set overrides the preferred order in which
  76. -- protocols are tested. (ie. "tcp", "udp")
  77. local bin = require "bin"
  78. local bit = require "bit"
  79. local datafiles = require "datafiles"
  80. local math = require "math"
  81. local nmap = require "nmap"
  82. local stdnse = require "stdnse"
  83. local string = require "string"
  84. local table = require "table"
  85. _ENV = stdnse.module("rpc", stdnse.seeall)
  86. -- Version 0.3
  87. --
  88. -- Created 01/24/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
  89. -- Revised 02/22/2010 - v0.2 - cleanup, revised the way TCP/UDP are handled fo
  90. -- encoding an decoding
  91. -- Revised 03/13/2010 - v0.3 - re-worked library to be OO
  92. -- Revised 04/18/2010 - v0.4 - Applied patch from Djalal Harouni with improved
  93. -- error checking and re-designed Comm class. see:
  94. -- http://seclists.org/nmap-dev/2010/q2/232
  95. -- Revised 06/02/2010 - v0.5 - added code to the Util class to check for file
  96. -- types and permissions.
  97. -- Revised 06/04/2010 - v0.6 - combined Portmap and RPC classes in the
  98. -- same Portmap class.
  99. --
  100. -- RPC args using the nmap.registry.args
  101. RPC_args = {
  102. ["rpcbind"] = { proto = 'rpc.protocol' },
  103. ["nfs"] = { ver = 'nfs.version' },
  104. ["mountd"] = { ver = 'mount.version' },
  105. }
  106. -- Defines the order in which to try to connect to the RPC programs
  107. -- TCP appears to be more stable than UDP in most cases, so try it first
  108. local RPC_PROTOCOLS = (nmap.registry.args and nmap.registry.args[RPC_args['rpcbind'].proto] and
  109. type(nmap.registry.args[RPC_args['rpcbind'].proto]) == 'table') and
  110. nmap.registry.args[RPC_args['rpcbind'].proto] or { "tcp", "udp" }
  111. -- used to cache the contents of the rpc datafile
  112. local RPC_PROGRAMS
  113. -- local mutex to synchronize I/O operations on nmap.registry[host.ip]['portmapper']
  114. local mutex = nmap.mutex("rpc")
  115. -- Supported protocol versions
  116. RPC_version = {
  117. ["rpcbind"] = { min=2, max=2 },
  118. ["nfs"] = { min=1, max=3 },
  119. ["mountd"] = { min=1, max=3 },
  120. }
  121. -- Low-level communication class
  122. Comm = {
  123. --- Creates a new rpc Comm object
  124. --
  125. -- @param program name string
  126. -- @param version number containing the program version to use
  127. -- @return a new Comm object
  128. new = function(self, program, version)
  129. local o = {}
  130. setmetatable(o, self)
  131. self.__index = self
  132. o.program = program
  133. o.program_id = Util.ProgNameToNumber(program)
  134. o.checkprogver = true
  135. o:SetVersion(version)
  136. return o
  137. end,
  138. --- Connects to the remote program
  139. --
  140. -- @param host table
  141. -- @param port table
  142. -- @param timeout [optional] socket timeout in ms
  143. -- @return status boolean true on success, false on failure
  144. -- @return string containing error message (if status is false)
  145. Connect = function(self, host, port, timeout)
  146. local status, err, socket
  147. status, err = self:ChkProgram()
  148. if (not(status)) then
  149. return status, err
  150. end
  151. status, err = self:ChkVersion()
  152. if (not(status)) then
  153. return status, err
  154. end
  155. timeout = timeout or stdnse.get_timeout(host, 10000)
  156. local new_socket = function(...)
  157. local socket = nmap.new_socket(...)
  158. socket:set_timeout(timeout)
  159. return socket
  160. end
  161. if ( port.protocol == "tcp" ) then
  162. if nmap.is_privileged() then
  163. -- Try to bind to a reserved port
  164. for i = 1, 10, 1 do
  165. local resvport = math.random(1, 1024)
  166. socket = new_socket()
  167. status, err = socket:bind(nil, resvport)
  168. if status then
  169. status, err = socket:connect(host, port)
  170. if status or err == "TIMEOUT" then break end
  171. socket:close()
  172. end
  173. end
  174. else
  175. socket = new_socket()
  176. status, err = socket:connect(host, port)
  177. end
  178. else
  179. if nmap.is_privileged() then
  180. -- Try to bind to a reserved port
  181. for i = 1, 10, 1 do
  182. local resvport = math.random(1, 1024)
  183. socket = new_socket("udp")
  184. status, err = socket:bind(nil, resvport)
  185. if status then
  186. status, err = socket:connect(host, port)
  187. if status or err == "TIMEOUT" then break end
  188. socket:close()
  189. end
  190. end
  191. else
  192. socket = new_socket("udp")
  193. status, err = socket:connect(host, port)
  194. end
  195. end
  196. if (not(status)) then
  197. return status, string.format("%s connect error: %s",
  198. self.program, err)
  199. else
  200. self.socket = socket
  201. self.host = host
  202. self.ip = host.ip
  203. self.port = port.number
  204. self.proto = port.protocol
  205. return status, nil
  206. end
  207. end,
  208. --- Disconnects from the remote program
  209. --
  210. -- @return status boolean true on success, false on failure
  211. -- @return string containing error message (if status is false)
  212. Disconnect = function(self)
  213. local status, err = self.socket:close()
  214. if (not(status)) then
  215. return status, string.format("%s disconnect error: %s",
  216. self.program, err)
  217. end
  218. self.socket=nil
  219. return status, nil
  220. end,
  221. --- Checks if the rpc program is supported
  222. --
  223. -- @return status boolean true on success, false on failure
  224. -- @return string containing error message (if status is false)
  225. ChkProgram = function(self)
  226. if (not(RPC_version[self.program])) then
  227. return false, string.format("RPC library does not support: %s protocol",
  228. self.program)
  229. end
  230. return true, nil
  231. end,
  232. --- Checks if the rpc program version is supported
  233. --
  234. -- @return status boolean true on success, false on failure
  235. -- @return string containing error message (if status is false)
  236. ChkVersion = function(self)
  237. if not self.checkprogver then return true end
  238. if ( self.version > RPC_version[self.program].max or
  239. self.version < RPC_version[self.program].min ) then
  240. return false, string.format("RPC library does not support: %s version %d",
  241. self.program,self.version)
  242. end
  243. return true, nil
  244. end,
  245. --- Sets the rpc program version
  246. --
  247. -- @return status boolean true
  248. SetVersion = function(self, version)
  249. if self.checkprogver then
  250. if (RPC_version[self.program] and RPC_args[self.program] and
  251. nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then
  252. self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver])
  253. elseif (not(self.version) and version) then
  254. self.version = version
  255. end
  256. else
  257. self.version = version
  258. end
  259. return true, nil
  260. end,
  261. --- Sets the verification of the specified program and version support
  262. -- before trying to connecting.
  263. -- @param check boolean to enable or disable checking of program and version support.
  264. SetCheckProgVer = function(self, check)
  265. self.checkprogver = check
  266. end,
  267. --- Sets the RPC program ID to use.
  268. -- @param progid number Program ID to set.
  269. SetProgID = function(self, progid)
  270. self.program_id = progid
  271. end,
  272. --- Checks if data contains enough bytes to read the <code>needed</code> amount
  273. -- If it doesn't it attempts to read the remaining amount of bytes from the socket
  274. --
  275. -- @param data string containing the current buffer
  276. -- @param pos number containing the current offset into the buffer
  277. -- @param needed number containing the number of bytes needed to be available
  278. -- @return status success or failure
  279. -- @return data string containing the data passed to the function and the additional data appended to it or error message on failure
  280. GetAdditionalBytes = function( self, data, pos, needed )
  281. local status, tmp
  282. if data:len() - pos + 1 < needed then
  283. local toread = needed - ( data:len() - pos + 1 )
  284. status, tmp = self.socket:receive_bytes( toread )
  285. if status then
  286. data = data .. tmp
  287. else
  288. return false, string.format("getAdditionalBytes() failed to read: %d bytes from the socket",
  289. needed - ( data:len() - pos ) )
  290. end
  291. end
  292. return true, data
  293. end,
  294. --- Creates a RPC header
  295. --
  296. -- @param xid number. If no xid was provided, a random one will be used.
  297. -- @param procedure number containing the procedure to call. Defaults to <code>0</code>.
  298. -- @param auth table containing the authentication data to use. Defaults to NULL authentication.
  299. -- @return status boolean true on success, false on failure
  300. -- @return string of bytes on success, error message on failure
  301. CreateHeader = function( self, xid, procedure, auth )
  302. local RPC_VERSION = 2
  303. local packet
  304. -- Defaulting to NULL Authentication
  305. local auth = auth or {type = Portmap.AuthType.NULL}
  306. local xid = xid or math.random(1234567890)
  307. local procedure = procedure or 0
  308. packet = bin.pack( ">IIIIII", xid, Portmap.MessageType.CALL, RPC_VERSION,
  309. self.program_id, self.version, procedure )
  310. if auth.type == Portmap.AuthType.NULL then
  311. packet = packet .. bin.pack( "IIII", 0, 0, 0, 0 )
  312. elseif auth.type == Portmap.AuthType.UNIX then
  313. packet = packet .. Util.marshall_int32(auth.type)
  314. local blob = (
  315. Util.marshall_int32(nmap.clock()) --time
  316. .. Util.marshall_vopaque(auth.hostname or 'localhost')
  317. .. Util.marshall_int32(auth.uid or 0)
  318. .. Util.marshall_int32(auth.gid or 0)
  319. )
  320. if auth.gids then --len prefix gid list
  321. blob = blob .. Util.marshall_int32(#auth.gids)
  322. for _,gid in ipairs(auth.gids) do
  323. blob = blob .. Util.marshall_int32(gid)
  324. end
  325. else
  326. blob = blob .. Util.marshall_int32(0)
  327. end
  328. packet = (packet .. Util.marshall_vopaque(blob)
  329. .. bin.pack( "II", 0, 0 ) --AUTH_NULL verf
  330. )
  331. else
  332. return false, "Comm.CreateHeader: invalid authentication type specified"
  333. end
  334. return true, packet
  335. end,
  336. --- Decodes the RPC header (without the leading 4 bytes as received over TCP)
  337. --
  338. -- @param data string containing the buffer of bytes read so far
  339. -- @param pos number containing the current offset into data
  340. -- @return pos number containing the offset after the decoding
  341. -- @return header table containing <code>xid</code>, <code>type</code>, <code>state</code>,
  342. -- <code>verifier</code> and ( <code>accept_state</code> or <code>denied_state</code> )
  343. DecodeHeader = function( self, data, pos )
  344. local header = {}
  345. local status
  346. local HEADER_LEN = 20
  347. header.verifier = {}
  348. if ( data:len() - pos < HEADER_LEN ) then
  349. local tmp
  350. status, tmp = self:GetAdditionalBytes( data, pos, HEADER_LEN - ( data:len() - pos ) )
  351. if not status then
  352. stdnse.debug4(
  353. string.format("Comm.DecodeHeader: failed to call GetAdditionalBytes"))
  354. return -1, nil
  355. end
  356. data = data .. tmp
  357. end
  358. pos, header.xid, header.type, header.state = bin.unpack(">III", data, pos)
  359. if ( header.state == Portmap.State.MSG_DENIED ) then
  360. pos, header.denied_state = bin.unpack(">I", data, pos )
  361. return pos, header
  362. end
  363. pos, header.verifier.flavor = bin.unpack(">I", data, pos)
  364. pos, header.verifier.length = bin.unpack(">I", data, pos)
  365. if header.verifier.length - 8 > 0 then
  366. status, data = self:GetAdditionalBytes( data, pos, header.verifier.length - 8 )
  367. if not status then
  368. stdnse.debug4(
  369. string.format("Comm.DecodeHeader: failed to call GetAdditionalBytes"))
  370. return -1, nil
  371. end
  372. pos, header.verifier.data = bin.unpack("A" .. header.verifier.length - 8, data, pos )
  373. end
  374. pos, header.accept_state = bin.unpack(">I", data, pos )
  375. return pos, header
  376. end,
  377. --- Reads the response from the socket
  378. --
  379. -- @return status true on success, false on failure
  380. -- @return data string containing the raw response or error message on failure
  381. ReceivePacket = function( self )
  382. local status
  383. if ( self.proto == "udp" ) then
  384. -- There's not much we can do in here to check if we received all data
  385. -- as the packet contains no length field. It's up to each decoding function
  386. -- to do appropriate checks
  387. return self.socket:receive_bytes(1)
  388. else
  389. local tmp, lastfragment, length
  390. local data, pos = "", 1
  391. -- Maximum number of allowed attempts to parse the received bytes. This
  392. -- prevents the code from looping endlessly on invalid content.
  393. local retries = 400
  394. repeat
  395. retries = retries - 1
  396. lastfragment = false
  397. status, data = self:GetAdditionalBytes( data, pos, 4 )
  398. if ( not(status) ) then
  399. return false, "Comm.ReceivePacket: failed to call GetAdditionalBytes"
  400. end
  401. pos, tmp = bin.unpack(">i", data, pos )
  402. length = bit.band( tmp, 0x7FFFFFFF )
  403. if ( bit.band( tmp, 0x80000000 ) == 0x80000000 ) then
  404. lastfragment = true
  405. end
  406. status, data = self:GetAdditionalBytes( data, pos, length )
  407. if ( not(status) ) then
  408. return false, "Comm.ReceivePacket: failed to call GetAdditionalBytes"
  409. end
  410. --
  411. -- When multiple packets are received they look like this
  412. -- H = Header data
  413. -- D = Data
  414. --
  415. -- We don't want the Header
  416. --
  417. -- HHHHDDDDDDDDDDDDDDHHHHDDDDDDDDDDD
  418. -- ^ ^ ^ ^
  419. -- 1 5 18 22
  420. --
  421. -- eg. we want
  422. -- data:sub(5, 18) and data:sub(22)
  423. --
  424. local bufcopy = data:sub(pos)
  425. if 1 ~= pos - 4 then
  426. bufcopy = data:sub(1, pos - 5) .. bufcopy
  427. pos = pos - 4
  428. else
  429. pos = 1
  430. end
  431. pos = pos + length
  432. data = bufcopy
  433. until (lastfragment == true) or (retries == 0)
  434. if retries == 0 then
  435. return false, "Aborted after too many retries"
  436. end
  437. return true, data
  438. end
  439. end,
  440. --- Encodes a RPC packet
  441. --
  442. -- @param xid number containing the transaction ID
  443. -- @param proc number containing the procedure to call
  444. -- @param auth table containing authentication information
  445. -- @param data string containing the packet data
  446. -- @return packet string containing the encoded packet data
  447. EncodePacket = function( self, xid, proc, auth, data )
  448. local status, packet = self:CreateHeader( xid, proc, auth )
  449. local len
  450. if ( not(status) ) then
  451. return
  452. end
  453. packet = packet .. ( data or "" )
  454. if ( self.proto == "udp") then
  455. return packet
  456. else
  457. -- set the high bit as this is our last fragment
  458. len = 0x80000000 + packet:len()
  459. return bin.pack(">I", len) .. packet
  460. end
  461. end,
  462. SendPacket = function( self, packet )
  463. if ( self.host and self.port ) then
  464. return self.socket:sendto(self.host, self.port, packet)
  465. else
  466. return self.socket:send( packet )
  467. end
  468. end,
  469. GetSocketInfo = function(self)
  470. return self.socket:get_info()
  471. end,
  472. }
  473. --- Portmap (rpcbind) class
  474. Portmap =
  475. {
  476. PROTOCOLS = {
  477. ['tcp'] = 6,
  478. ['udp'] = 17,
  479. },
  480. -- TODO: add more Authentication Protocols
  481. AuthType =
  482. {
  483. NULL = 0,
  484. UNIX = 1,
  485. },
  486. -- TODO: complete Authentication stats and error messages
  487. AuthState =
  488. {
  489. AUTH_OK = 0,
  490. AUTH_BADCRED = 1,
  491. AUTH_REJECTEDCRED = 2,
  492. AUTH_BADVERF = 3,
  493. AUTH_REJECTEDVERF = 4,
  494. AUTH_TOOWEAK = 5,
  495. AUTH_INVALIDRESP = 6,
  496. AUTH_FAILED = 7,
  497. },
  498. AuthMsg =
  499. {
  500. [0] = "Success.",
  501. [1] = "bad credential (seal broken).",
  502. [2] = "client must begin new session.",
  503. [3] = "bad verifier (seal broken).",
  504. [4] = "verifier expired or replayed.",
  505. [5] = "rejected for security reasons.",
  506. [6] = "bogus response verifier.",
  507. [7] = "reason unknown.",
  508. },
  509. MessageType =
  510. {
  511. CALL = 0,
  512. REPLY = 1
  513. },
  514. Procedure =
  515. {
  516. [2] =
  517. {
  518. GETPORT = 3,
  519. DUMP = 4,
  520. CALLIT = 5,
  521. },
  522. },
  523. State =
  524. {
  525. MSG_ACCEPTED = 0,
  526. MSG_DENIED = 1,
  527. },
  528. AcceptState =
  529. {
  530. SUCCESS = 0,
  531. PROG_UNAVAIL = 1,
  532. PROG_MISMATCH = 2,
  533. PROC_UNAVAIL = 3,
  534. GARBAGE_ARGS = 4,
  535. SYSTEM_ERR = 5,
  536. },
  537. AcceptMsg =
  538. {
  539. [0] = "RPC executed successfully.",
  540. [1] = "remote hasn't exported program.",
  541. [2] = "remote can't support version.",
  542. [3] = "program can't support procedure.",
  543. [4] = "procedure can't decode params.",
  544. [5] = "errors like memory allocation failure.",
  545. },
  546. RejectState =
  547. {
  548. RPC_MISMATCH = 0,
  549. AUTH_ERROR = 1,
  550. },
  551. RejectMsg =
  552. {
  553. [0] = "RPC version number != 2.",
  554. [1] = "remote can't authenticate caller.",
  555. },
  556. new = function(self,o)
  557. o = o or {}
  558. setmetatable(o, self)
  559. self.__index = self
  560. return o
  561. end,
  562. --- Dumps a list of RCP programs from the portmapper
  563. --
  564. -- @param comm object handles rpc program information and
  565. -- low-level packet manipulation
  566. -- @return status boolean true on success, false on failure
  567. -- @return result table containing RPC program information or error message
  568. -- on failure. The table has the following format:
  569. --
  570. -- <code>
  571. -- table[program_id][protocol]["port"] = <port number>
  572. -- table[program_id][protocol]["version"] = <table of versions>
  573. -- </code>
  574. --
  575. -- Where
  576. -- o program_id is the number associated with the program
  577. -- o protocol is either "tcp" or "udp"
  578. --
  579. Dump = function(self, comm)
  580. local status, data, packet, response, pos, header
  581. local program_table = setmetatable({}, { __mode = 'v' })
  582. packet = comm:EncodePacket( nil, Portmap.Procedure[comm.version].DUMP,
  583. { type=Portmap.AuthType.NULL }, data )
  584. if (not(comm:SendPacket(packet))) then
  585. return false, "Portmap.Dump: Failed to send data"
  586. end
  587. status, data = comm:ReceivePacket()
  588. if ( not(status) ) then
  589. return false, "Portmap.Dump: Failed to read data from socket"
  590. end
  591. pos, header = comm:DecodeHeader( data, 1 )
  592. if ( not(header) ) then
  593. return false, "Portmap.Dump: Failed to decode RPC header"
  594. end
  595. if header.type ~= Portmap.MessageType.REPLY then
  596. return false, "Portmap.Dump: Packet was not a reply"
  597. end
  598. if header.state ~= Portmap.State.MSG_ACCEPTED then
  599. if (Portmap.RejectMsg[header.denied_state]) then
  600. return false,
  601. string.format("Portmap.Dump: RPC call failed: %s",
  602. Portmap.RejectMsg[header.denied_state])
  603. else
  604. return false,
  605. string.format("Portmap.Dump: RPC call failed: code %d",
  606. header.state)
  607. end
  608. end
  609. if header.accept_state ~= Portmap.AcceptState.SUCCESS then
  610. if (Portmap.AcceptMsg[header.accept_state]) then
  611. return false,
  612. string.format("Portmap.Dump: RPC accepted state: %s",
  613. Portmap.AcceptMsg[header.accept_state])
  614. else
  615. return false,
  616. string.format("Portmap.Dump: RPC accepted state code %d",
  617. header.accept_state)
  618. end
  619. end
  620. while true do
  621. local vfollows
  622. local program, version, protocol, port
  623. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  624. if ( not(status) ) then
  625. return false, "Portmap.Dump: Failed to call GetAdditionalBytes"
  626. end
  627. pos, vfollows = bin.unpack( ">I", data, pos )
  628. if ( vfollows == 0 ) then
  629. break
  630. end
  631. pos, program, version, protocol, port = bin.unpack(">IIII", data, pos)
  632. if ( protocol == Portmap.PROTOCOLS.tcp ) then
  633. protocol = "tcp"
  634. elseif ( protocol == Portmap.PROTOCOLS.udp ) then
  635. protocol = "udp"
  636. end
  637. program_table[program] = program_table[program] or {}
  638. program_table[program][protocol] = program_table[program][protocol] or {}
  639. program_table[program][protocol]["port"] = port
  640. program_table[program][protocol]["version"] = program_table[program][protocol]["version"] or {}
  641. table.insert( program_table[program][protocol]["version"], version )
  642. -- parts of the code rely on versions being in order
  643. -- this way the highest version can be chosen by choosing the last element
  644. table.sort( program_table[program][protocol]["version"] )
  645. end
  646. nmap.registry[comm.ip]['portmapper'] = program_table
  647. return true, nmap.registry[comm.ip]['portmapper']
  648. end,
  649. --- Calls the portmap callit call and returns the raw response
  650. --
  651. -- @param comm object handles rpc program information and
  652. -- low-level packet manipulation
  653. -- @param program string name of the program
  654. -- @param protocol string containing either "tcp" or "udp"
  655. -- @param version number containing the version of the queried program
  656. -- @return status true on success, false on failure
  657. -- @return data string containing the raw response
  658. Callit = function( self, comm, program, protocol, version )
  659. if ( not( Portmap.PROTOCOLS[protocol] ) ) then
  660. return false, ("Portmap.Callit: Protocol %s not supported"):format(protocol)
  661. end
  662. if ( Util.ProgNameToNumber(program) == nil ) then
  663. return false, ("Portmap.Callit: Unknown program name: %s"):format(program)
  664. end
  665. local data = bin.pack(">IIII", Util.ProgNameToNumber(program), version, 0, 0 )
  666. local packet = comm:EncodePacket(nil, Portmap.Procedure[comm.version].CALLIT,
  667. { type=Portmap.AuthType.NULL }, data )
  668. if (not(comm:SendPacket(packet))) then
  669. return false, "Portmap.Callit: Failed to send data"
  670. end
  671. data = ""
  672. local status, data = comm:ReceivePacket()
  673. if ( not(status) ) then
  674. return false, "Portmap.Callit: Failed to read data from socket"
  675. end
  676. local pos, header = comm:DecodeHeader( data, 1 )
  677. if ( not(header) ) then
  678. return false, "Portmap.Callit: Failed to decode RPC header"
  679. end
  680. if header.type ~= Portmap.MessageType.REPLY then
  681. return false, "Portmap.Callit: Packet was not a reply"
  682. end
  683. return true, data
  684. end,
  685. --- Queries the portmapper for the port of the selected program,
  686. -- protocol and version
  687. --
  688. -- @param comm object handles rpc program information and
  689. -- low-level packet manipulation
  690. -- @param program string name of the program
  691. -- @param protocol string containing either "tcp" or "udp"
  692. -- @param version number containing the version of the queried program
  693. -- @return number containing the port number
  694. GetPort = function( self, comm, program, protocol, version )
  695. local status, data, response, header, pos, packet
  696. local xid
  697. if ( not( Portmap.PROTOCOLS[protocol] ) ) then
  698. return false, ("Portmap.GetPort: Protocol %s not supported"):format(protocol)
  699. end
  700. if ( Util.ProgNameToNumber(program) == nil ) then
  701. return false, ("Portmap.GetPort: Unknown program name: %s"):format(program)
  702. end
  703. data = bin.pack(">I>I>I>I", Util.ProgNameToNumber(program), version,
  704. Portmap.PROTOCOLS[protocol], 0 )
  705. packet = comm:EncodePacket(xid, Portmap.Procedure[comm.version].GETPORT,
  706. { type=Portmap.AuthType.NULL }, data )
  707. if (not(comm:SendPacket(packet))) then
  708. return false, "Portmap.GetPort: Failed to send data"
  709. end
  710. data = ""
  711. status, data = comm:ReceivePacket()
  712. if ( not(status) ) then
  713. return false, "Portmap.GetPort: Failed to read data from socket"
  714. end
  715. pos, header = comm:DecodeHeader( data, 1 )
  716. if ( not(header) ) then
  717. return false, "Portmap.GetPort: Failed to decode RPC header"
  718. end
  719. if header.type ~= Portmap.MessageType.REPLY then
  720. return false, "Portmap.GetPort: Packet was not a reply"
  721. end
  722. if header.state ~= Portmap.State.MSG_ACCEPTED then
  723. if (Portmap.RejectMsg[header.denied_state]) then
  724. return false, string.format("Portmap.GetPort: RPC call failed: %s",
  725. Portmap.RejectMsg[header.denied_state])
  726. else
  727. return false,
  728. string.format("Portmap.GetPort: RPC call failed: code %d",
  729. header.state)
  730. end
  731. end
  732. if header.accept_state ~= Portmap.AcceptState.SUCCESS then
  733. if (Portmap.AcceptMsg[header.accept_state]) then
  734. return false, string.format("Portmap.GetPort: RPC accepted state: %s",
  735. Portmap.AcceptMsg[header.accept_state])
  736. else
  737. return false, string.format("Portmap.GetPort: RPC accepted state code %d",
  738. header.accept_state)
  739. end
  740. end
  741. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  742. if ( not(status) ) then
  743. return false, "Portmap.GetPort: Failed to call GetAdditionalBytes"
  744. end
  745. return true, select(2, bin.unpack(">I", data, pos ) )
  746. end,
  747. }
  748. --- Mount class handling communication with the mountd program
  749. --
  750. -- Currently supports versions 1 through 3
  751. -- Can be called either directly or through the static Helper class
  752. --
  753. Mount = {
  754. StatMsg = {
  755. [1] = "Not owner.",
  756. [2] = "No such file or directory.",
  757. [5] = "I/O error.",
  758. [13] = "Permission denied.",
  759. [20] = "Not a directory.",
  760. [22] = "Invalid argument.",
  761. [63] = "Filename too long.",
  762. [10004] = "Operation not supported.",
  763. [10006] = "A failure on the server.",
  764. },
  765. StatCode = {
  766. MNT_OK = 0,
  767. MNTERR_PERM = 1,
  768. MNTERR_NOENT = 2,
  769. MNTERR_IO = 5,
  770. MNTERR_ACCES = 13,
  771. MNTERR_NOTDIR = 20,
  772. MNTERR_INVAL = 22,
  773. MNTERR_NAMETOOLONG = 63,
  774. MNTERR_NOTSUPP = 10004,
  775. MNTERR_SERVERFAULT = 10006,
  776. },
  777. Procedure =
  778. {
  779. MOUNT = 1,
  780. DUMP = 2,
  781. UMNT = 3,
  782. UMNTALL = 4,
  783. EXPORT = 5,
  784. },
  785. new = function(self,o)
  786. o = o or {}
  787. setmetatable(o, self)
  788. self.__index = self
  789. return o
  790. end,
  791. --- Requests a list of NFS export from the remote server
  792. --
  793. -- @param comm object handles rpc program information and
  794. -- low-level packet manipulation
  795. -- @return status success or failure
  796. -- @return entries table containing a list of share names (strings)
  797. Export = function(self, comm)
  798. local msg_type = 0
  799. local packet
  800. local pos = 1
  801. local header = {}
  802. local entries = {}
  803. local data = ""
  804. local status
  805. if comm.proto ~= "tcp" and comm.proto ~= "udp" then
  806. return false, "Mount.Export: Protocol should be either udp or tcp"
  807. end
  808. packet = comm:EncodePacket(nil, Mount.Procedure.EXPORT,
  809. { type=Portmap.AuthType.UNIX }, nil )
  810. if (not(comm:SendPacket( packet ))) then
  811. return false, "Mount.Export: Failed to send data"
  812. end
  813. status, data = comm:ReceivePacket()
  814. if ( not(status) ) then
  815. return false, "Mount.Export: Failed to read data from socket"
  816. end
  817. -- make sure we have at least 24 bytes to unpack the header
  818. status, data = comm:GetAdditionalBytes( data, pos, 24 )
  819. if (not(status)) then
  820. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  821. end
  822. pos, header = comm:DecodeHeader( data, pos )
  823. if not header then
  824. return false, "Mount.Export: Failed to decode header"
  825. end
  826. if header.type ~= Portmap.MessageType.REPLY then
  827. return false, "Mount.Export: packet was not a reply"
  828. end
  829. if header.state ~= Portmap.State.MSG_ACCEPTED then
  830. if (Portmap.RejectMsg[header.denied_state]) then
  831. return false, string.format("Mount.Export: RPC call failed: %s",
  832. Portmap.RejectMsg[header.denied_state])
  833. else
  834. return false, string.format("Mount.Export: RPC call failed: code %d",
  835. header.state)
  836. end
  837. end
  838. if header.accept_state ~= Portmap.AcceptState.SUCCESS then
  839. if (Portmap.AcceptMsg[header.accept_state]) then
  840. return false, string.format("Mount.Export: RPC accepted state: %s",
  841. Portmap.AcceptMsg[header.accept_state])
  842. else
  843. return false, string.format("Mount.Export: RPC accepted state code %d",
  844. header.accept_state)
  845. end
  846. end
  847. -- Decode directory entries
  848. --
  849. -- [entry]
  850. -- 4 bytes - value follows (1 if more data, 0 if not)
  851. -- [Directory]
  852. -- 4 bytes - value len
  853. -- len bytes - directory name
  854. -- ? bytes - fill bytes (see calcFillByte)
  855. -- [Groups]
  856. -- 4 bytes - value follows (1 if more data, 0 if not)
  857. -- [Group] (1 or more)
  858. -- 4 bytes - group len
  859. -- len bytes - group value
  860. -- ? bytes - fill bytes (see calcFillByte)
  861. while true do
  862. -- make sure we have atleast 4 more bytes to check for value follows
  863. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  864. if (not(status)) then
  865. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  866. end
  867. local data_follows
  868. pos, data_follows = Util.unmarshall_uint32(data, pos)
  869. if data_follows ~= 1 then
  870. break
  871. end
  872. --- Export list entry starts here
  873. local entry = {}
  874. local len
  875. -- make sure we have atleast 4 more bytes to get the length
  876. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  877. if (not(status)) then
  878. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  879. end
  880. pos, len = Util.unmarshall_uint32(data, pos)
  881. status, data = comm:GetAdditionalBytes( data, pos, len )
  882. if (not(status)) then
  883. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  884. end
  885. pos, entry.name = Util.unmarshall_vopaque(len, data, pos)
  886. -- decode groups
  887. while true do
  888. local group
  889. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  890. if (not(status)) then
  891. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  892. end
  893. pos, data_follows = Util.unmarshall_uint32(data, pos)
  894. if data_follows ~= 1 then
  895. break
  896. end
  897. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  898. if (not(status)) then
  899. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  900. end
  901. pos, len = Util.unmarshall_uint32(data, pos)
  902. status, data = comm:GetAdditionalBytes( data, pos, len )
  903. if (not(status)) then
  904. return false, "Mount.Export: Failed to call GetAdditionalBytes"
  905. end
  906. pos, group = Util.unmarshall_vopaque(len, data, pos)
  907. table.insert( entry, group )
  908. end
  909. table.insert(entries, entry)
  910. end
  911. return true, entries
  912. end,
  913. --- Attempts to mount a remote export in order to get the filehandle
  914. --
  915. -- @param comm object handles rpc program information and
  916. -- low-level packet manipulation
  917. -- @param path string containing the path to mount
  918. -- @return status success or failure
  919. -- @return fhandle string containing the filehandle of the remote export
  920. Mount = function(self, comm, path)
  921. local packet, mount_status
  922. local _, pos, data, header, fhandle = "", 1, "", "", {}
  923. local status, len
  924. data = Util.marshall_vopaque(path)
  925. packet = comm:EncodePacket( nil, Mount.Procedure.MOUNT, { type=Portmap.AuthType.UNIX }, data )
  926. if (not(comm:SendPacket(packet))) then
  927. return false, "Mount: Failed to send data"
  928. end
  929. status, data = comm:ReceivePacket()
  930. if ( not(status) ) then
  931. return false, "Mount: Failed to read data from socket"
  932. end
  933. pos, header = comm:DecodeHeader( data, pos )
  934. if not header then
  935. return false, "Mount: Failed to decode header"
  936. end
  937. if header.type ~= Portmap.MessageType.REPLY then
  938. return false, "Mount: Packet was not a reply"
  939. end
  940. if header.state ~= Portmap.State.MSG_ACCEPTED then
  941. if (Portmap.RejectMsg[header.denied_state]) then
  942. return false, string.format("Mount: RPC call failed: %s",
  943. Portmap.RejectMsg[header.denied_state])
  944. else
  945. return false, string.format("Mount: RPC call failed: code %d",
  946. header.state)
  947. end
  948. end
  949. if header.accept_state ~= Portmap.AcceptState.SUCCESS then
  950. if (Portmap.AcceptMsg[header.accept_state]) then
  951. return false, string.format("Mount (%s): RPC accepted state: %s",
  952. path, Portmap.AcceptMsg[header.accept_state])
  953. else
  954. return false, string.format("Mount (%s): RPC accepted state code %d",
  955. path, header.accept_state)
  956. end
  957. end
  958. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  959. if (not(status)) then
  960. return false, "Mount: Failed to call GetAdditionalBytes"
  961. end
  962. pos, mount_status = Util.unmarshall_uint32(data, pos)
  963. if (mount_status ~= Mount.StatCode.MNT_OK) then
  964. if (Mount.StatMsg[mount_status]) then
  965. return false, string.format("Mount failed: %s",Mount.StatMsg[mount_status])
  966. else
  967. return false, string.format("Mount failed: code %d", mount_status)
  968. end
  969. end
  970. if ( comm.version == 3 ) then
  971. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  972. if (not(status)) then
  973. return false, "Mount: Failed to call GetAdditionalBytes"
  974. end
  975. _, len = bin.unpack(">I", data, pos )
  976. status, data = comm:GetAdditionalBytes( data, pos, len + 4 )
  977. if (not(status)) then
  978. return false, "Mount: Failed to call GetAdditionalBytes"
  979. end
  980. pos, fhandle = bin.unpack( "A" .. len + 4, data, pos )
  981. elseif ( comm.version < 3 ) then
  982. status, data = comm:GetAdditionalBytes( data, pos, 32 )
  983. if (not(status)) then
  984. return false, "Mount: Failed to call GetAdditionalBytes"
  985. end
  986. pos, fhandle = bin.unpack( "A32", data, pos )
  987. else
  988. return false, "Mount failed"
  989. end
  990. return true, fhandle
  991. end,
  992. --- Attempts to unmount a remote export in order to get the filehandle
  993. --
  994. -- @param comm object handles rpc program information and
  995. -- low-level packet manipulation
  996. -- @param path string containing the path to mount
  997. -- @return status success or failure
  998. -- @return error string containing error if status is false
  999. Unmount = function(self, comm, path)
  1000. local packet, status
  1001. local _, pos, data, header, fhandle = "", 1, "", "", {}
  1002. data = Util.marshall_vopaque(path)
  1003. packet = comm:EncodePacket( nil, Mount.Procedure.UMNT, { type=Portmap.AuthType.UNIX }, data )
  1004. if (not(comm:SendPacket(packet))) then
  1005. return false, "Unmount: Failed to send data"
  1006. end
  1007. status, data = comm:ReceivePacket( )
  1008. if ( not(status) ) then
  1009. return false, "Unmount: Failed to read data from socket"
  1010. end
  1011. pos, header = comm:DecodeHeader( data, pos )
  1012. if not header then
  1013. return false, "Unmount: Failed to decode header"
  1014. end
  1015. if header.type ~= Portmap.MessageType.REPLY then
  1016. return false, "Unmount: Packet was not a reply"
  1017. end
  1018. if header.state ~= Portmap.State.MSG_ACCEPTED then
  1019. if (Portmap.RejectMsg[header.denied_state]) then
  1020. return false, string.format("Unmount: RPC call failed: %s",
  1021. Portmap.RejectMsg[header.denied_state])
  1022. else
  1023. return false, string.format("Unmount: RPC call failed: code %d",
  1024. header.state)
  1025. end
  1026. end
  1027. if header.accept_state ~= Portmap.AcceptState.SUCCESS then
  1028. if (Portmap.AcceptMsg[header.accept_state]) then
  1029. return false, string.format("Unmount (%s): RPC accepted state: %s",
  1030. path, Portmap.AcceptMsg[header.accept_state])
  1031. else
  1032. return false, string.format("Unmount (%s): RPC accepted state code %d",
  1033. path, header.accept_state)
  1034. end
  1035. end
  1036. return true, ""
  1037. end,
  1038. }
  1039. --- NFS class handling communication with the nfsd program
  1040. --
  1041. -- Currently supports versions 1 through 3
  1042. -- Can be called either directly or through the static Helper class
  1043. --
  1044. NFS = {
  1045. -- NFS error msg v2 and v3
  1046. StatMsg = {
  1047. [1] = "Not owner.",
  1048. [2] = "No such file or directory.",
  1049. [5] = "I/O error.",
  1050. [6] = "I/O error. No such device or address.",
  1051. [13] = "Permission denied.",
  1052. [17] = "File exists.",
  1053. [18] = "Attempt to do a cross-device hard link.",
  1054. [19] = "No such device.",
  1055. [20] = "Not a directory.",
  1056. [21] = "Is a directory.",
  1057. [22] = "Invalid argument or unsupported argument for an operation.",
  1058. [27] = "File too large.",
  1059. [28] = "No space left on device.",
  1060. [30] = "Read-only file system.",
  1061. [31] = "Too many hard links.",
  1062. [63] = "The filename in an operation was too long.",
  1063. [66] = "An attempt was made to remove a directory that was not empty.",
  1064. [69] = "Resource (quota) hard limit exceeded.",
  1065. [70] = "Invalid file handle.",
  1066. [71] = "Too many levels of remote in path.",
  1067. [99] = "The server's write cache used in the \"WRITECACHE\" call got flushed to disk.",
  1068. [10001] = "Illegal NFS file handle.",
  1069. [10002] = "Update synchronization mismatch was detected during a SETATTR operation.",
  1070. [10003] = "READDIR or READDIRPLUS cookie is stale.",
  1071. [10004] = "Operation is not supported.",
  1072. [10005] = "Buffer or request is too small.",
  1073. [10006] = "An error occurred on the server which does not map to any of the legal NFS version 3 protocol error values.",
  1074. [10007] = "An attempt was made to create an object of a type not supported by the server.",
  1075. [10008] = "The server initiated the request, but was not able to complete it in a timely fashion.",
  1076. },
  1077. StatCode = {
  1078. -- NFS Version 1
  1079. [1] = {
  1080. NFS_OK = 0,
  1081. NFSERR_PERM = 1,
  1082. NFSERR_NOENT = 2,
  1083. NFSERR_IO = 5,
  1084. NFSERR_NXIO = 6,
  1085. NFSERR_ACCES = 13,
  1086. NFSERR_EXIST = 17,
  1087. NFSERR_NODEV = 19,
  1088. NFSERR_NOTDIR = 20,
  1089. NFSERR_ISDIR = 21,
  1090. NFSERR_FBIG = 27,
  1091. NFSERR_NOSPC = 28,
  1092. NFSERR_ROFS = 30,
  1093. NFSERR_NAMETOOLONG = 63,
  1094. NFSERR_NOTEMPTY = 66,
  1095. NFSERR_DQUOT = 69,
  1096. NFSERR_STALE = 70,
  1097. NFSERR_WFLUSH = 99,
  1098. },
  1099. -- NFS Version 2
  1100. [2] = {
  1101. NFS_OK = 0,
  1102. NFSERR_PERM = 1,
  1103. NFSERR_NOENT = 2,
  1104. NFSERR_IO = 5,
  1105. NFSERR_NXIO = 6,
  1106. NFSERR_ACCES = 13,
  1107. NFSERR_EXIST = 17,
  1108. NFSERR_NODEV = 19,
  1109. NFSERR_NOTDIR = 20,
  1110. NFSERR_ISDIR = 21,
  1111. NFSERR_FBIG = 27,
  1112. NFSERR_NOSPC = 28,
  1113. NFSERR_ROFS = 30,
  1114. NFSERR_NAMETOOLONG = 63,
  1115. NFSERR_NOTEMPTY = 66,
  1116. NFSERR_DQUOT = 69,
  1117. NFSERR_STALE = 70,
  1118. NFSERR_WFLUSH = 99,
  1119. },
  1120. -- NFS Version 3
  1121. [3] = {
  1122. NFS_OK = 0,
  1123. NFSERR_PERM = 1,
  1124. NFSERR_NOENT = 2,
  1125. NFSERR_IO = 5,
  1126. NFSERR_NXIO = 6,
  1127. NFSERR_ACCES = 13,
  1128. NFSERR_EXIST = 17,
  1129. NFSERR_XDEV = 18,
  1130. NFSERR_NODEV = 19,
  1131. NFSERR_NOTDIR = 20,
  1132. NFSERR_ISDIR = 21,
  1133. NFSERR_INVAL = 22,
  1134. NFSERR_FBIG = 27,
  1135. NFSERR_NOSPC = 28,
  1136. NFSERR_ROFS = 30,
  1137. NFSERR_MLINK = 31,
  1138. NFSERR_NAMETOOLONG = 63,
  1139. NFSERR_NOTEMPTY = 66,
  1140. NFSERR_DQUOT = 69,
  1141. NFSERR_STALE = 70,
  1142. NFSERR_REMOTE = 71,
  1143. NFSERR_BADHANDLE = 10001,
  1144. NFSERR_NOT_SYNC = 10002,
  1145. NFSERR_BAD_COOKIE = 10003,
  1146. NFSERR_NOTSUPP = 10004,
  1147. NFSERR_TOOSMALL = 10005,
  1148. NFSERR_SERVERFAULT = 10006,
  1149. NFSERR_BADTYPE = 10007,
  1150. NFSERR_JUKEBOX = 10008,
  1151. },
  1152. },
  1153. -- Unfortunately the NFS procedure numbers differ in between versions
  1154. Procedure =
  1155. {
  1156. -- NFS Version 1
  1157. [1] =
  1158. {
  1159. GETATTR = 1,
  1160. ROOT = 3,
  1161. LOOKUP = 4,
  1162. EXPORT = 5,
  1163. READDIR = 16,
  1164. STATFS = 17,
  1165. },
  1166. -- NFS Version 2
  1167. [2] =
  1168. {
  1169. GETATTR = 1,
  1170. ROOT = 3,
  1171. LOOKUP = 4,
  1172. EXPORT = 5,
  1173. READDIR = 16,
  1174. STATFS = 17,
  1175. },
  1176. -- NFS Version 3
  1177. [3] =
  1178. {
  1179. GETATTR = 1,
  1180. SETATTR = 2,
  1181. LOOKUP = 3,
  1182. ACCESS = 4,
  1183. EXPORT = 5,
  1184. READDIR = 16,
  1185. READDIRPLUS = 17,
  1186. FSSTAT = 18,
  1187. FSINFO = 19,
  1188. PATHCONF = 20,
  1189. COMMIT = 21,
  1190. },
  1191. },
  1192. -- ACCESS values used to check the bit mask.
  1193. AccessBits =
  1194. {
  1195. [3] =
  1196. {
  1197. ACCESS_READ = 0x0001,
  1198. ACCESS_LOOKUP = 0x0002,
  1199. ACCESS_MODIFY = 0x0004,
  1200. ACCESS_EXTEND = 0x0008,
  1201. ACCESS_DELETE = 0x0010,
  1202. ACCESS_EXECUTE = 0x0020,
  1203. },
  1204. },
  1205. FSinfoBits =
  1206. {
  1207. [3] =
  1208. {
  1209. FSF_LINK = 0x0001,
  1210. FSF_SYMLINK = 0x0002,
  1211. FSF_HOMOGENEOUS = 0x0008,
  1212. FSF_CANSETTIME = 0x0010,
  1213. },
  1214. },
  1215. new = function(self,o)
  1216. o = o or {}
  1217. setmetatable(o, self)
  1218. self.__index = self
  1219. return o
  1220. end,
  1221. CheckStat = function (self, procedurename, version, status)
  1222. if (status ~= NFS.StatCode[version].NFS_OK) then
  1223. if (NFS.StatMsg[status]) then
  1224. stdnse.debug4(
  1225. string.format("%s failed: %s", procedurename, NFS.StatMsg[status]))
  1226. else
  1227. stdnse.debug4(
  1228. string.format("%s failed: code %d", procedurename, status))
  1229. end
  1230. return false
  1231. end
  1232. return true
  1233. end,
  1234. AccessRead = function (self, mask, version)
  1235. return bit.band(mask, NFS.AccessBits[version].ACCESS_READ)
  1236. end,
  1237. AccessLookup = function (self, mask, version)
  1238. return bit.band(mask, NFS.AccessBits[version].ACCESS_LOOKUP)
  1239. end,
  1240. AccessModify = function (self, mask, version)
  1241. return bit.band(mask, NFS.AccessBits[version].ACCESS_MODIFY)
  1242. end,
  1243. AccessExtend = function (self, mask, version)
  1244. return bit.band(mask, NFS.AccessBits[version].ACCESS_EXTEND)
  1245. end,
  1246. AccessDelete = function (self, mask, version)
  1247. return bit.band(mask, NFS.AccessBits[version].ACCESS_DELETE)
  1248. end,
  1249. AccessExecute = function (self, mask, version)
  1250. return bit.band(mask, NFS.AccessBits[version].ACCESS_EXECUTE)
  1251. end,
  1252. FSinfoLink = function(self, mask, version)
  1253. return bit.band(mask, NFS.FSinfoBits[version].FSF_LINK)
  1254. end,
  1255. FSinfoSymlink = function(self, mask, version)
  1256. return bit.band(mask, NFS.FSinfoBits[version].FSF_SYMLINK)
  1257. end,
  1258. FSinfoHomogeneous = function(self, mask, version)
  1259. return bit.band(mask, NFS.FSinfoBits[version].FSF_HOMOGENEOUS)
  1260. end,
  1261. FSinfoCansettime = function(self, mask, version)
  1262. return bit.band(mask, NFS.FSinfoBits[version].FSF_CANSETTIME)
  1263. end,
  1264. --- Decodes the READDIR section of a NFS ReadDir response
  1265. --
  1266. -- @param comm object handles rpc program information and
  1267. -- low-level packet manipulation
  1268. -- @param data string containing the buffer of bytes read so far
  1269. -- @param pos number containing the current offset into data
  1270. -- @return pos number containing the offset after the decoding
  1271. -- @return entries table containing two table entries <code>attributes</code>
  1272. -- and <code>entries</code>. The attributes entry is only present when
  1273. -- using NFS version 3. The <code>entries</code> field contain one
  1274. -- table for each file/directory entry. It has the following fields
  1275. -- <code>file_id</code>, <code>name</code> and <code>cookie</code>
  1276. --
  1277. ReadDirDecode = function( self, comm, data, pos )
  1278. local response = {}
  1279. local value_follows
  1280. local status, _
  1281. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1282. if (not(status)) then
  1283. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1284. return -1, nil
  1285. end
  1286. pos, status = Util.unmarshall_uint32(data, pos)
  1287. if (not self:CheckStat("READDIR", comm.version, status)) then
  1288. return -1, nil
  1289. end
  1290. if ( 3 == comm.version ) then
  1291. local attrib = {}
  1292. response.attributes = {}
  1293. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1294. if (not(status)) then
  1295. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1296. return -1, nil
  1297. end
  1298. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1299. if value_follows == 0 then
  1300. return -1, nil
  1301. end
  1302. status, data = comm:GetAdditionalBytes( data, pos, 84 )
  1303. if (not(status)) then
  1304. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1305. return -1, nil
  1306. end
  1307. pos, attrib = Util.unmarshall_nfsattr(data, pos, comm.version)
  1308. table.insert(response.attributes, attrib)
  1309. -- opaque data
  1310. status, data = comm:GetAdditionalBytes( data, pos, 8 )
  1311. if (not(status)) then
  1312. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1313. return -1, nil
  1314. end
  1315. pos, _ = bin.unpack(">L", data, pos)
  1316. end
  1317. response.entries = {}
  1318. while true do
  1319. local entry = {}
  1320. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1321. if (not(status)) then
  1322. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1323. return -1, nil
  1324. end
  1325. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1326. if ( value_follows == 0 ) then
  1327. break
  1328. end
  1329. if ( 3 == comm.version ) then
  1330. status, data = comm:GetAdditionalBytes( data, pos, 8 )
  1331. if (not(status)) then
  1332. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1333. return -1, nil
  1334. end
  1335. pos, entry.fileid = Util.unmarshall_uint64(data, pos )
  1336. else
  1337. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1338. if (not(status)) then
  1339. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1340. return -1, nil
  1341. end
  1342. pos, entry.fileid = Util.unmarshall_uint32(data, pos)
  1343. end
  1344. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1345. if (not(status)) then
  1346. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1347. return -1, nil
  1348. end
  1349. pos, entry.length = Util.unmarshall_uint32(data, pos)
  1350. status, data = comm:GetAdditionalBytes( data, pos, entry.length )
  1351. if (not(status)) then
  1352. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1353. return -1, nil
  1354. end
  1355. pos, entry.name = Util.unmarshall_vopaque(entry.length, data, pos)
  1356. if ( 3 == comm.version ) then
  1357. status, data = comm:GetAdditionalBytes( data, pos, 8 )
  1358. if (not(status)) then
  1359. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1360. return -1, nil
  1361. end
  1362. pos, entry.cookie = Util.unmarshall_uint64(data, pos)
  1363. else
  1364. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1365. if (not(status)) then
  1366. stdnse.debug4("NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
  1367. return -1, nil
  1368. end
  1369. pos, entry.cookie = Util.unmarshall_uint32(data, pos)
  1370. end
  1371. table.insert( response.entries, entry )
  1372. end
  1373. return pos, response
  1374. end,
  1375. --- Reads the contents inside a NFS directory
  1376. --
  1377. -- @param comm object handles rpc program information and
  1378. -- low-level packet manipulation
  1379. -- @param file_handle string containing the filehandle to query
  1380. -- @return status true on success, false on failure
  1381. -- @return table of file table entries as described in <code>decodeReadDir</code>
  1382. ReadDir = function( self, comm, file_handle )
  1383. local status, packet
  1384. local cookie, count = 0, 8192
  1385. local pos, data, _ = 1, "", ""
  1386. local header, response = {}, {}
  1387. if ( not(file_handle) ) then
  1388. return false, "ReadDir: No filehandle received"
  1389. end
  1390. if ( comm.version == 3 ) then
  1391. local opaque_data = 0
  1392. data = bin.pack("A>L>L>I", file_handle, cookie, opaque_data, count)
  1393. else
  1394. data = bin.pack("A>I>I", file_handle, cookie, count)
  1395. end
  1396. packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].READDIR,
  1397. { type=Portmap.AuthType.UNIX }, data )
  1398. if(not(comm:SendPacket( packet ))) then
  1399. return false, "ReadDir: Failed to send data"
  1400. end
  1401. status, data = comm:ReceivePacket()
  1402. if ( not(status) ) then
  1403. return false, "ReadDir: Failed to read data from socket"
  1404. end
  1405. pos, header = comm:DecodeHeader( data, pos )
  1406. if not header then
  1407. return false, "ReadDir: Failed to decode header"
  1408. end
  1409. pos, response = self:ReadDirDecode( comm, data, pos )
  1410. if (not(response)) then
  1411. return false, "ReadDir: Failed to decode the READDIR section"
  1412. end
  1413. return true, response
  1414. end,
  1415. LookUpDecode = function(self, comm, data, pos)
  1416. local lookup, status, len, value_follows, _ = {}
  1417. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1418. if not status then
  1419. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1420. return -1, nil
  1421. end
  1422. pos, status = Util.unmarshall_uint32(data, pos)
  1423. if (not self:CheckStat("LOOKUP", comm.version, status)) then
  1424. return -1, nil
  1425. end
  1426. if (comm.version == 3) then
  1427. status, data = comm:GetAdditionalBytes( data, pos, 4)
  1428. if (not(status)) then
  1429. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1430. return -1, nil
  1431. end
  1432. _, len = Util.unmarshall_uint32(data, pos)
  1433. status, data = comm:GetAdditionalBytes( data, pos, len + 4)
  1434. if (not(status)) then
  1435. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1436. return -1, nil
  1437. end
  1438. pos, lookup.fhandle = bin.unpack( "A" .. len + 4, data, pos)
  1439. status, data = comm:GetAdditionalBytes( data, pos, 4)
  1440. if (not(status)) then
  1441. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1442. return -1, nil
  1443. end
  1444. lookup.attributes = {}
  1445. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1446. if (value_follows ~= 0) then
  1447. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1448. if (not(status)) then
  1449. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1450. return -1, nil
  1451. end
  1452. pos, lookup.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1453. else
  1454. stdnse.debug4("NFS.LookUpDecode: File Attributes follow failed")
  1455. end
  1456. status, data = comm:GetAdditionalBytes( data, pos, 4)
  1457. if (not(status)) then
  1458. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1459. return -1, nil
  1460. end
  1461. lookup.dir_attributes = {}
  1462. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1463. if (value_follows ~= 0) then
  1464. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1465. if (not(status)) then
  1466. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1467. return -1, nil
  1468. end
  1469. pos, lookup.dir_attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1470. else
  1471. stdnse.debug4("NFS.LookUpDecode: File Attributes follow failed")
  1472. end
  1473. elseif (comm.version < 3) then
  1474. status, data = comm:GetAdditionalBytes( data, pos, 32)
  1475. if (not(status)) then
  1476. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1477. return -1, nil
  1478. end
  1479. pos, lookup.fhandle = bin.unpack("A32", data, pos)
  1480. status, data = comm:GetAdditionalBytes( data, pos, 64 )
  1481. if (not(status)) then
  1482. stdnse.debug4("NFS.LookUpDecode: Failed to call GetAdditionalBytes")
  1483. return -1, nil
  1484. end
  1485. pos, lookup.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1486. else
  1487. stdnse.debug1("NFS.LookUpDecode: NFS unsupported version %d", comm.version)
  1488. return -1, nil
  1489. end
  1490. return pos, lookup
  1491. end,
  1492. LookUp = function(self, comm, dir_handle, file)
  1493. local status, packet
  1494. local pos, data = 1, ""
  1495. local header, response = {}, {}
  1496. if (not(dir_handle)) then
  1497. return false, "LookUp: No dirhandle received"
  1498. end
  1499. data = Util.marshall_opaque(dir_handle) .. Util.marshall_vopaque(file)
  1500. packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].LOOKUP,
  1501. {type=Portmap.AuthType.UNIX}, data)
  1502. if(not(comm:SendPacket(packet))) then
  1503. return false, "LookUp: Failed to send data"
  1504. end
  1505. status, data = comm:ReceivePacket()
  1506. if ( not(status) ) then
  1507. return false, "LookUp: Failed to read data from socket"
  1508. end
  1509. pos, header = comm:DecodeHeader(data, pos)
  1510. if not header then
  1511. return false, "LookUp: Failed to decode header"
  1512. end
  1513. pos, response = self:LookUpDecode(comm, data, pos)
  1514. if (not(response)) then
  1515. return false, "LookUp: Failed to decode the LOOKUP section"
  1516. end
  1517. return true, response
  1518. end,
  1519. ReadDirPlusDecode = function(self, comm, data, pos)
  1520. local response, status, value_follows, _ = {}
  1521. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1522. if not status then
  1523. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1524. return -1, nil
  1525. end
  1526. pos, status = Util.unmarshall_uint32(data, pos)
  1527. if (not self:CheckStat("READDIRPLUS", comm.version, status)) then
  1528. return -1, nil
  1529. end
  1530. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1531. if not status then
  1532. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1533. return -1, nil
  1534. end
  1535. pos, value_follows = bin.unpack(">I", data, pos)
  1536. if value_follows == 0 then
  1537. stdnse.debug4("NFS.ReadDirPlusDecode: Attributes follow failed")
  1538. return -1, nil
  1539. end
  1540. status, data = comm:GetAdditionalBytes( data, pos, 84 )
  1541. if not status then
  1542. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1543. return -1, nil
  1544. end
  1545. response.attributes = {}
  1546. pos, response.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1547. status, data = comm:GetAdditionalBytes(data, pos, 8)
  1548. if not status then
  1549. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1550. return -1, nil
  1551. end
  1552. pos, _ = bin.unpack(">L", data, pos)
  1553. response.entries = {}
  1554. while true do
  1555. local entry, len = {}
  1556. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1557. if not status then
  1558. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1559. return -1, nil
  1560. end
  1561. pos, value_follows = bin.unpack(">I", data, pos)
  1562. if (value_follows == 0) then
  1563. break
  1564. end
  1565. status, data = comm:GetAdditionalBytes(data, pos, 8)
  1566. if not status then
  1567. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1568. return -1, nil
  1569. end
  1570. pos, entry.fileid = bin.unpack(">L", data, pos)
  1571. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1572. if not status then
  1573. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1574. return -1, nil
  1575. end
  1576. pos, entry.length = bin.unpack(">I", data, pos)
  1577. status, data = comm:GetAdditionalBytes( data, pos, entry.length )
  1578. if not status then
  1579. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1580. return -1, nil
  1581. end
  1582. pos, entry.name = Util.unmarshall_vopaque(entry.length, data, pos)
  1583. status, data = comm:GetAdditionalBytes(data, pos, 8)
  1584. if not status then
  1585. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1586. return -1, nil
  1587. end
  1588. pos, entry.cookie = bin.unpack(">L", data, pos)
  1589. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1590. if not status then
  1591. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1592. return -1, nil
  1593. end
  1594. entry.attributes = {}
  1595. pos, value_follows = bin.unpack(">I", data, pos)
  1596. if (value_follows ~= 0) then
  1597. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1598. if not status then
  1599. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1600. return -1, nil
  1601. end
  1602. pos, entry.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1603. else
  1604. stdnse.debug4("NFS.ReadDirPlusDecode: %s Attributes follow failed",
  1605. entry.name)
  1606. end
  1607. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1608. if not status then
  1609. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1610. return -1, nil
  1611. end
  1612. entry.fhandle = ""
  1613. pos, value_follows = bin.unpack(">I", data, pos)
  1614. if (value_follows ~= 0) then
  1615. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1616. if not status then
  1617. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1618. return -1, nil
  1619. end
  1620. _, len = bin.unpack(">I", data, pos)
  1621. status, data = comm:GetAdditionalBytes(data, pos, len + 4)
  1622. if not status then
  1623. stdnse.debug4("NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
  1624. return -1, nil
  1625. end
  1626. pos, entry.fhandle = bin.unpack( "A" .. len + 4, data, pos )
  1627. else
  1628. stdnse.debug4("NFS.ReadDirPlusDecode: %s handle follow failed",
  1629. entry.name)
  1630. end
  1631. table.insert(response.entries, entry)
  1632. end
  1633. return pos, response
  1634. end,
  1635. ReadDirPlus = function(self, comm, file_handle)
  1636. local status, packet
  1637. local cookie, opaque_data, dircount, maxcount = 0, 0, 512, 8192
  1638. local pos, data = 1, ""
  1639. local header, response = {}, {}
  1640. if (comm.version < 3) then
  1641. return false, string.format("NFS version: %d does not support ReadDirPlus",
  1642. comm.version)
  1643. end
  1644. if not file_handle then
  1645. return false, "ReadDirPlus: No filehandle received"
  1646. end
  1647. data = bin.pack("A>L>L>I>I", file_handle, cookie,
  1648. opaque_data, dircount, maxcount)
  1649. packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].READDIRPLUS,
  1650. {type = Portmap.AuthType.UNIX }, data)
  1651. if (not(comm:SendPacket(packet))) then
  1652. return false, "ReadDirPlus: Failed to send data"
  1653. end
  1654. status, data = comm:ReceivePacket()
  1655. if not status then
  1656. return false, "ReadDirPlus: Failed to read data from socket"
  1657. end
  1658. pos, header = comm:DecodeHeader( data, pos )
  1659. if not header then
  1660. return false, "ReadDirPlus: Failed to decode header"
  1661. end
  1662. pos, response = self:ReadDirPlusDecode( comm, data, pos )
  1663. if not response then
  1664. return false, "ReadDirPlus: Failed to decode the READDIR section"
  1665. end
  1666. return true, response
  1667. end,
  1668. FsStatDecode = function(self, comm, data, pos)
  1669. local fsstat, status, value_follows = {}
  1670. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1671. if not status then
  1672. stdnse.debug4("NFS.FsStatDecode: Failed to call GetAdditionalBytes")
  1673. return -1, nil
  1674. end
  1675. pos, status = Util.unmarshall_uint32(data, pos)
  1676. if (not self:CheckStat("FSSTAT", comm.version, status)) then
  1677. return -1, nil
  1678. end
  1679. fsstat.attributes = {}
  1680. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1681. if (value_follows ~= 0) then
  1682. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1683. if not status then
  1684. stdnse.debug4("NFS.FsStatDecode: Failed to call GetAdditionalBytes")
  1685. return -1, nil
  1686. end
  1687. pos, fsstat.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1688. else
  1689. stdnse.debug4("NFS.FsStatDecode: Attributes follow failed")
  1690. end
  1691. status, data = comm:GetAdditionalBytes( data, pos, 52)
  1692. if not status then
  1693. stdnse.debug4("NFS.FsStatDecode: Failed to call GetAdditionalBytes")
  1694. return -1, nil
  1695. end
  1696. pos, fsstat.tbytes, fsstat.fbytes, fsstat.abytes, fsstat.tfiles,
  1697. fsstat.ffiles, fsstat.afiles = Util.unmarshall_nfssize3(data, pos, 6)
  1698. pos, fsstat.invarsec = Util.unmarshall_uint32(data, pos)
  1699. return pos, fsstat
  1700. end,
  1701. FsStat = function(self, comm, file_handle)
  1702. local status, packet
  1703. local pos, data = 1, ""
  1704. local header, response = {}, {}
  1705. if (comm.version < 3) then
  1706. return false, string.format("NFS version: %d does not support FSSTAT",
  1707. comm.version)
  1708. end
  1709. if not file_handle then
  1710. return false, "FsStat: No filehandle received"
  1711. end
  1712. packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].FSSTAT,
  1713. {type = Portmap.AuthType.UNIX}, file_handle)
  1714. if (not(comm:SendPacket(packet))) then
  1715. return false, "FsStat: Failed to send data"
  1716. end
  1717. status, data = comm:ReceivePacket()
  1718. if not status then
  1719. return false, "FsStat: Failed to read data from socket"
  1720. end
  1721. pos, header = comm:DecodeHeader(data, pos)
  1722. if not header then
  1723. return false, "FsStat: Failed to decode header"
  1724. end
  1725. pos, response = self:FsStatDecode(comm, data, pos)
  1726. if not response then
  1727. return false, "FsStat: Failed to decode the FSSTAT section"
  1728. end
  1729. return true, response
  1730. end,
  1731. FsInfoDecode = function(self, comm, data, pos)
  1732. local fsinfo, status, value_follows = {}
  1733. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1734. if not status then
  1735. stdnse.debug4("NFS.FsInfoDecode: Failed to call GetAdditionalBytes")
  1736. return -1, nil
  1737. end
  1738. pos, status = Util.unmarshall_uint32(data, pos)
  1739. if (not self:CheckStat("FSINFO", comm.version, status)) then
  1740. return -1, nil
  1741. end
  1742. fsinfo.attributes = {}
  1743. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1744. if (value_follows ~= 0) then
  1745. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1746. if not status then
  1747. stdnse.debug4("NFS.FsInfoDecode: Failed to call GetAdditionalBytes")
  1748. return -1, nil
  1749. end
  1750. pos, fsinfo.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1751. else
  1752. stdnse.debug4("NFS.FsInfoDecode: Attributes follow failed")
  1753. end
  1754. status, data = comm:GetAdditionalBytes(data, pos, 48)
  1755. if not status then
  1756. stdnse.debug4("NFS.FsStatDecode: Failed to call GetAdditionalBytes")
  1757. return -1, nil
  1758. end
  1759. pos, fsinfo.rtmax, fsinfo.rtpref, fsinfo.rtmult,
  1760. fsinfo.wtmax, fsinfo.wtpref, fsinfo.wtmult,
  1761. fsinfo.dtpref = Util.unmarshall_uint32(data, pos, 7)
  1762. pos, fsinfo.maxfilesize = Util.unmarshall_nfssize3(data, pos)
  1763. pos, fsinfo.time_delta = Util.unmarshall_nfstime(data, pos)
  1764. pos, fsinfo.properties = Util.unmarshall_uint32(data, pos)
  1765. return pos, fsinfo
  1766. end,
  1767. FsInfo = function(self, comm, file_handle)
  1768. local status, packet
  1769. local pos, data = 1, ""
  1770. local header, response = {}
  1771. if (comm.version < 3) then
  1772. return false, string.format("NFS version: %d does not support FSINFO",
  1773. comm.version)
  1774. end
  1775. if not file_handle then
  1776. return false, "FsInfo: No filehandle received"
  1777. end
  1778. data = Util.marshall_opaque(file_handle)
  1779. packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].FSINFO,
  1780. {type = Portmap.AuthType.UNIX}, data)
  1781. if (not(comm:SendPacket(packet))) then
  1782. return false, "FsInfo: Failed to send data"
  1783. end
  1784. status, data = comm:ReceivePacket()
  1785. if not status then
  1786. return false, "FsInfo: Failed to read data from socket"
  1787. end
  1788. pos, header = comm:DecodeHeader(data, pos)
  1789. if not header then
  1790. return false, "FsInfo: Failed to decode header"
  1791. end
  1792. pos, response = self:FsInfoDecode(comm, data, pos)
  1793. if not response then
  1794. return false, "FsInfo: Failed to decode the FSINFO section"
  1795. end
  1796. return true, response
  1797. end,
  1798. PathConfDecode = function(self, comm, data, pos)
  1799. local pconf, status, value_follows = {}
  1800. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1801. if not status then
  1802. stdnse.debug4("NFS.PathConfDecode: Failed to call GetAdditionalBytes")
  1803. return -1, nil
  1804. end
  1805. pos, status = Util.unmarshall_uint32(data, pos)
  1806. if (not self:CheckStat("PATHCONF", comm.version, status)) then
  1807. return -1, nil
  1808. end
  1809. pconf.attributes = {}
  1810. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1811. if (value_follows ~= 0) then
  1812. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1813. if not status then
  1814. stdnse.debug4("NFS.PathConfDecode: Failed to call GetAdditionalBytes")
  1815. return -1, nil
  1816. end
  1817. pos, pconf.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1818. else
  1819. stdnse.debug4("NFS.PathConfDecode: Attributes follow failed")
  1820. end
  1821. status, data = comm:GetAdditionalBytes(data, pos, 24)
  1822. if not status then
  1823. stdnse.debug4("NFS.PathConfDecode: Failed to call GetAdditionalBytes")
  1824. return -1, nil
  1825. end
  1826. pos, pconf.linkmax, pconf.name_max, pconf.no_trunc,
  1827. pconf.chown_restricted, pconf.case_insensitive,
  1828. pconf.case_preserving = Util.unmarshall_uint32(data, pos, 6)
  1829. return pos, pconf
  1830. end,
  1831. PathConf = function(self, comm, file_handle)
  1832. local status, packet
  1833. local pos, data = 1, ""
  1834. local header, response = {}
  1835. if (comm.version < 3) then
  1836. return false, string.format("NFS version: %d does not support PATHCONF",
  1837. comm.version)
  1838. end
  1839. if not file_handle then
  1840. return false, "PathConf: No filehandle received"
  1841. end
  1842. data = Util.marshall_opaque(file_handle)
  1843. packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].PATHCONF,
  1844. {type = Portmap.AuthType.UNIX}, data)
  1845. if (not(comm:SendPacket(packet))) then
  1846. return false, "PathConf: Failed to send data"
  1847. end
  1848. status, data = comm:ReceivePacket()
  1849. if not status then
  1850. return false, "PathConf: Failed to read data from socket"
  1851. end
  1852. pos, header = comm:DecodeHeader(data, pos)
  1853. if not header then
  1854. return false, "PathConf: Failed to decode header"
  1855. end
  1856. pos, response = self:PathConfDecode(comm, data, pos)
  1857. if not response then
  1858. return false, "PathConf: Failed to decode the PATHCONF section"
  1859. end
  1860. return true, response
  1861. end,
  1862. AccessDecode = function(self, comm, data, pos)
  1863. local access, status, value_follows = {}
  1864. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1865. if not status then
  1866. stdnse.debug4("NFS.AccessDecode: Failed to call GetAdditionalBytes")
  1867. return -1, nil
  1868. end
  1869. pos, status = Util.unmarshall_uint32(data, pos)
  1870. if (not self:CheckStat("ACCESS", comm.version, status)) then
  1871. return -1, nil
  1872. end
  1873. access.attributes = {}
  1874. pos, value_follows = Util.unmarshall_uint32(data, pos)
  1875. if (value_follows ~= 0) then
  1876. status, data = comm:GetAdditionalBytes(data, pos, 84)
  1877. if not status then
  1878. stdnse.debug4("NFS.AccessDecode: Failed to call GetAdditionalBytes")
  1879. return -1, nil
  1880. end
  1881. pos, access.attributes = Util.unmarshall_nfsattr(data, pos, comm.version)
  1882. else
  1883. stdnse.debug4("NFS.AccessDecode: Attributes follow failed")
  1884. end
  1885. status, data = comm:GetAdditionalBytes(data, pos, 4)
  1886. if not status then
  1887. stdnse.debug4("NFS.AccessDecode: Failed to call GetAdditionalBytes")
  1888. return -1, nil
  1889. end
  1890. pos, access.mask = Util.unmarshall_uint32(data, pos)
  1891. return pos, access
  1892. end,
  1893. Access = function(self, comm, file_handle, access)
  1894. local status, packet
  1895. local pos, data = 1, ""
  1896. local header, response = {}, {}
  1897. if (comm.version < 3) then
  1898. return false, string.format("NFS version: %d does not support ACCESS",
  1899. comm.version)
  1900. end
  1901. if not file_handle then
  1902. return false, "Access: No filehandle received"
  1903. end
  1904. data = Util.marshall_opaque(file_handle) .. Util.marshall_uint32(access)
  1905. packet = comm:EncodePacket(nil, NFS.Procedure[comm.version].ACCESS,
  1906. {type = Portmap.AuthType.UNIX}, data)
  1907. if (not(comm:SendPacket(packet))) then
  1908. return false, "Access: Failed to send data"
  1909. end
  1910. status, data = comm:ReceivePacket()
  1911. if not status then
  1912. return false, "Access: Failed to read data from socket"
  1913. end
  1914. pos, header = comm:DecodeHeader(data, pos)
  1915. if not header then
  1916. return false, "Access: Failed to decode header"
  1917. end
  1918. pos, response = self:AccessDecode(comm, data, pos)
  1919. if not response then
  1920. return false, "Access: Failed to decode the FSSTAT section"
  1921. end
  1922. return true, response
  1923. end,
  1924. --- Gets filesystem stats (Total Blocks, Free Blocks and Available block) on a remote NFS share
  1925. --
  1926. -- @param comm object handles rpc program information and
  1927. -- low-level packet manipulation
  1928. -- @param file_handle string containing the filehandle to query
  1929. -- @return status true on success, false on failure
  1930. -- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
  1931. -- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
  1932. -- @return errormsg if status is false
  1933. StatFs = function( self, comm, file_handle )
  1934. local status, packet
  1935. local pos, data, _ = 1, "", ""
  1936. local header, statfs = {}, {}
  1937. if ( comm.version > 2 ) then
  1938. return false, ("StatFs: Version %d not supported"):format(comm.version)
  1939. end
  1940. if ( not(file_handle) or file_handle:len() ~= 32 ) then
  1941. return false, "StatFs: Incorrect filehandle received"
  1942. end
  1943. data = Util.marshall_opaque(file_handle)
  1944. packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].STATFS, { type=Portmap.AuthType.UNIX }, data )
  1945. if (not(comm:SendPacket( packet ))) then
  1946. return false, "StatFS: Failed to send data"
  1947. end
  1948. status, data = comm:ReceivePacket( )
  1949. if ( not(status) ) then
  1950. return false, "StatFs: Failed to read data from socket"
  1951. end
  1952. pos, header = comm:DecodeHeader( data, pos )
  1953. if not header then
  1954. return false, "StatFs: Failed to decode header"
  1955. end
  1956. pos, statfs = self:StatFsDecode( comm, data, pos )
  1957. if not statfs then
  1958. return false, "StatFs: Failed to decode statfs structure"
  1959. end
  1960. return true, statfs
  1961. end,
  1962. --- Attempts to decode the attributes section of the reply
  1963. --
  1964. -- @param comm object handles rpc program information and
  1965. -- low-level packet manipulation
  1966. -- @param data string containing the full statfs reply
  1967. -- @param pos number pointing to the statfs section of the reply
  1968. -- @return pos number containing the offset after decoding
  1969. -- @return statfs table with the following fields: <code>type</code>, <code>mode</code>,
  1970. -- <code>nlink</code>, <code>uid</code>, <code>gid</code>, <code>size</code>,
  1971. -- <code>blocksize</code>, <code>rdev</code>, <code>blocks</code>, <code>fsid</code>,
  1972. -- <code>fileid</code>, <code>atime</code>, <code>mtime</code> and <code>ctime</code>
  1973. --
  1974. GetAttrDecode = function( self, comm, data, pos )
  1975. local status
  1976. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  1977. if (not(status)) then
  1978. stdnse.debug4("GetAttrDecode: Failed to call GetAdditionalBytes")
  1979. return -1, nil
  1980. end
  1981. pos, status = Util.unmarshall_uint32(data, pos)
  1982. if (not self:CheckStat("GETATTR", comm.version, status)) then
  1983. return -1, nil
  1984. end
  1985. if ( comm.version < 3 ) then
  1986. status, data = comm:GetAdditionalBytes( data, pos, 64 )
  1987. elseif (comm.version == 3) then
  1988. status, data = comm:GetAdditionalBytes( data, pos, 84 )
  1989. else
  1990. stdnse.debug4("GetAttrDecode: Unsupported version")
  1991. return -1, nil
  1992. end
  1993. if ( not(status) ) then
  1994. stdnse.debug4("GetAttrDecode: Failed to call GetAdditionalBytes")
  1995. return -1, nil
  1996. end
  1997. return Util.unmarshall_nfsattr(data, pos, comm.version)
  1998. end,
  1999. --- Gets mount attributes (uid, gid, mode, etc ..) from a remote NFS share
  2000. --
  2001. -- @param comm object handles rpc program information and
  2002. -- low-level packet manipulation
  2003. -- @param file_handle string containing the filehandle to query
  2004. -- @return status true on success, false on failure
  2005. -- @return attribs table with the fields <code>type</code>, <code>mode</code>,
  2006. -- <code>nlink</code>, <code>uid</code>, <code>gid</code>, <code>size</code>,
  2007. -- <code>blocksize</code>, <code>rdev</code>, <code>blocks</code>, <code>fsid</code>,
  2008. -- <code>fileid</code>, <code>atime</code>, <code>mtime</code> and <code>ctime</code>
  2009. -- @return errormsg if status is false
  2010. GetAttr = function( self, comm, file_handle )
  2011. local data, packet, status, attribs, pos, header
  2012. data = Util.marshall_opaque(file_handle)
  2013. packet = comm:EncodePacket( nil, NFS.Procedure[comm.version].GETATTR, { type=Portmap.AuthType.UNIX }, data )
  2014. if(not(comm:SendPacket(packet))) then
  2015. return false, "GetAttr: Failed to send data"
  2016. end
  2017. status, data = comm:ReceivePacket()
  2018. if ( not(status) ) then
  2019. return false, "GetAttr: Failed to read data from socket"
  2020. end
  2021. pos, header = comm:DecodeHeader( data, 1 )
  2022. if not header then
  2023. return false, "GetAttr: Failed to decode header"
  2024. end
  2025. pos, attribs = self:GetAttrDecode(comm, data, pos )
  2026. if not attribs then
  2027. return false, "GetAttr: Failed to decode attrib structure"
  2028. end
  2029. return true, attribs
  2030. end,
  2031. --- Attempts to decode the StatFS section of the reply
  2032. --
  2033. -- @param comm object handles rpc program information and
  2034. -- low-level packet manipulation
  2035. -- @param data string containing the full statfs reply
  2036. -- @param pos number pointing to the statfs section of the reply
  2037. -- @return pos number containing the offset after decoding
  2038. -- @return statfs table with the following fields: <code>transfer_size</code>, <code>block_size</code>,
  2039. -- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
  2040. StatFsDecode = function( self, comm, data, pos )
  2041. local status
  2042. local statfs = {}
  2043. status, data = comm:GetAdditionalBytes( data, pos, 4 )
  2044. if (not(status)) then
  2045. stdnse.debug4("StatFsDecode: Failed to call GetAdditionalBytes")
  2046. return -1, nil
  2047. end
  2048. pos, status = Util.unmarshall_uint32(data, pos)
  2049. if (not self:CheckStat("STATFS", comm.version, status)) then
  2050. return -1, nil
  2051. end
  2052. status, data = comm:GetAdditionalBytes( data, pos, 20 )
  2053. if (not(status)) then
  2054. stdnse.debug4("StatFsDecode: Failed to call GetAdditionalBytes")
  2055. return -1, nil
  2056. end
  2057. pos, statfs.transfer_size, statfs.block_size,
  2058. statfs.total_blocks, statfs.free_blocks,
  2059. statfs.available_blocks = Util.unmarshall_uint32(data, pos, 5)
  2060. return pos, statfs
  2061. end,
  2062. }
  2063. Helper = {
  2064. --- Lists the NFS exports on the remote host
  2065. -- This function abstracts the RPC communication with the portmapper from the user
  2066. --
  2067. -- @param host table
  2068. -- @param port table
  2069. -- @return status true on success, false on failure
  2070. -- @return result table of string entries or error message on failure
  2071. ShowMounts = function( host, port )
  2072. local status, result, mounts
  2073. local mountd, mnt_comm
  2074. local mnt = Mount:new()
  2075. local portmap = Portmap:new()
  2076. status, mountd = Helper.GetProgramInfo( host, port, "mountd")
  2077. if ( not(status) ) then
  2078. stdnse.debug4("rpc.Helper.ShowMounts: GetProgramInfo failed")
  2079. return status, "rpc.Helper.ShowMounts: GetProgramInfo failed"
  2080. end
  2081. mnt_comm = Comm:new('mountd', mountd.version)
  2082. status, result = mnt_comm:Connect(host, mountd.port)
  2083. if ( not(status) ) then
  2084. stdnse.debug4("rpc.Helper.ShowMounts: %s", result)
  2085. return false, result
  2086. end
  2087. status, mounts = mnt:Export(mnt_comm)
  2088. mnt_comm:Disconnect()
  2089. if ( not(status) ) then
  2090. stdnse.debug4("rpc.Helper.ShowMounts: %s", mounts)
  2091. end
  2092. return status, mounts
  2093. end,
  2094. --- Mounts a remote NFS export and returns the file handle
  2095. --
  2096. -- This is a high level function to be used by NSE scripts
  2097. -- To close the mounted NFS export use UnmountPath() function
  2098. --
  2099. -- @param host table
  2100. -- @param port table
  2101. -- @param path string containing the path to mount
  2102. -- @return on success a Comm object which can be
  2103. -- used later as a parameter by low level Mount
  2104. -- functions, on failure returns nil.
  2105. -- @return on success the filehandle of the NFS export as
  2106. -- a string, on failure returns the error message.
  2107. MountPath = function(host, port, path)
  2108. local fhandle, status, err
  2109. local mountd, mnt_comm
  2110. local mnt = Mount:new()
  2111. status, mountd = Helper.GetProgramInfo( host, port, "mountd")
  2112. if not status then
  2113. stdnse.debug4("rpc.Helper.MountPath: GetProgramInfo failed")
  2114. return nil, "rpc.Helper.MountPath: GetProgramInfo failed"
  2115. end
  2116. mnt_comm = Comm:new("mountd", mountd.version)
  2117. status, err = mnt_comm:Connect(host, mountd.port)
  2118. if not status then
  2119. stdnse.debug4("rpc.Helper.MountPath: %s", err)
  2120. return nil, err
  2121. end
  2122. status, fhandle = mnt:Mount(mnt_comm, path)
  2123. if not status then
  2124. mnt_comm:Disconnect()
  2125. stdnse.debug4("rpc.Helper.MountPath: %s", fhandle)
  2126. return nil, fhandle
  2127. end
  2128. return mnt_comm, fhandle
  2129. end,
  2130. --- Unmounts a remote mounted NFS export
  2131. --
  2132. -- This is a high level function to be used by NSE scripts
  2133. -- This function must be used to unmount a NFS point
  2134. -- mounted by MountPath()
  2135. --
  2136. -- @param mnt_comm object returned from a previous call to
  2137. -- MountPath()
  2138. -- @param path string containing the path to unmount
  2139. -- @return true on success or nil on failure
  2140. -- @return error message on failure
  2141. UnmountPath = function(mnt_comm, path)
  2142. local mnt = Mount:new()
  2143. local status, ret = mnt:Unmount(mnt_comm, path)
  2144. mnt_comm:Disconnect()
  2145. if not status then
  2146. stdnse.debug4("rpc.Helper.UnmountPath: %s", ret)
  2147. return nil, ret
  2148. end
  2149. return status, nil
  2150. end,
  2151. --- Connects to a remote NFS server
  2152. --
  2153. -- This is a high level function to open NFS connections
  2154. -- To close the NFS connection use NfsClose() function
  2155. --
  2156. -- @param host table
  2157. -- @param port table
  2158. -- @return on success a Comm object which can be
  2159. -- used later as a parameter by low level NFS
  2160. -- functions, on failure returns nil.
  2161. -- @return error message on failure.
  2162. NfsOpen = function(host, port)
  2163. local nfs_comm, nfsd, status, err
  2164. status, nfsd = Helper.GetProgramInfo(host, port, "nfs")
  2165. if not status then
  2166. stdnse.debug4("rpc.Helper.NfsOpen: GetProgramInfo failed")
  2167. return nil, "rpc.Helper.NfsOpen: GetProgramInfo failed"
  2168. end
  2169. nfs_comm = Comm:new('nfs', nfsd.version)
  2170. status, err = nfs_comm:Connect(host, nfsd.port)
  2171. if not status then
  2172. stdnse.debug4("rpc.Helper.NfsProc: %s", err)
  2173. return nil, err
  2174. end
  2175. return nfs_comm, nil
  2176. end,
  2177. --- Closes the NFS connection
  2178. --
  2179. -- This is a high level function to close NFS connections
  2180. -- This function must be used to close the NFS connection
  2181. -- opened by the NfsOpen() call
  2182. --
  2183. -- @param nfs_comm object returned by NfsOpen()
  2184. -- @return true on success or nil on failure
  2185. -- @return error message on failure
  2186. NfsClose = function(nfs_comm)
  2187. local status, ret = nfs_comm:Disconnect()
  2188. if not status then
  2189. stdnse.debug4("rpc.Helper.NfsClose: %s", ret)
  2190. return nil, ret
  2191. end
  2192. return status, nil
  2193. end,
  2194. --- Retrieves NFS storage statistics
  2195. --
  2196. -- @param host table
  2197. -- @param port table
  2198. -- @param path string containing the nfs export path
  2199. -- @return status true on success, false on failure
  2200. -- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
  2201. -- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
  2202. ExportStats = function( host, port, path )
  2203. local fhandle
  2204. local stats, status, result
  2205. local mnt_comm, nfs_comm
  2206. local mountd, nfsd = {}, {}
  2207. local mnt, nfs = Mount:new(), NFS:new()
  2208. status, mountd = Helper.GetProgramInfo( host, port, "mountd", 2)
  2209. if ( not(status) ) then
  2210. stdnse.debug4("rpc.Helper.ExportStats: GetProgramInfo failed")
  2211. return status, "rpc.Helper.ExportStats: GetProgramInfo failed"
  2212. end
  2213. status, nfsd = Helper.GetProgramInfo( host, port, "nfs", 2)
  2214. if ( not(status) ) then
  2215. stdnse.debug4("rpc.Helper.ExportStats: GetProgramInfo failed")
  2216. return status, "rpc.Helper.ExportStats: GetProgramInfo failed"
  2217. end
  2218. mnt_comm = Comm:new('mountd', mountd.version)
  2219. nfs_comm = Comm:new('nfs', nfsd.version)
  2220. -- TODO: recheck the version mismatch when adding NFSv4
  2221. if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
  2222. stdnse.debug4("rpc.Helper.ExportStats: versions mismatch, nfs v%d - mount v%d",
  2223. nfs_comm.version, mnt_comm.version)
  2224. return false, string.format("versions mismatch, nfs v%d - mount v%d",
  2225. nfs_comm.version, mnt_comm.version)
  2226. end
  2227. status, result = mnt_comm:Connect(host, mountd.port)
  2228. if ( not(status) ) then
  2229. stdnse.debug4("rpc.Helper.ExportStats: %s", result)
  2230. return status, result
  2231. end
  2232. status, result = nfs_comm:Connect(host, nfsd.port)
  2233. if ( not(status) ) then
  2234. mnt_comm:Disconnect()
  2235. stdnse.debug4("rpc.Helper.ExportStats: %s", result)
  2236. return status, result
  2237. end
  2238. status, fhandle = mnt:Mount(mnt_comm, path)
  2239. if ( not(status) ) then
  2240. mnt_comm:Disconnect()
  2241. nfs_comm:Disconnect()
  2242. stdnse.debug4("rpc.Helper.ExportStats: %s", fhandle)
  2243. return status, fhandle
  2244. end
  2245. status, stats = nfs:StatFs(nfs_comm, fhandle)
  2246. if ( not(status) ) then
  2247. mnt_comm:Disconnect()
  2248. nfs_comm:Disconnect()
  2249. stdnse.debug4("rpc.Helper.ExportStats: %s", stats)
  2250. return status, stats
  2251. end
  2252. status, fhandle = mnt:Unmount(mnt_comm, path)
  2253. mnt_comm:Disconnect()
  2254. nfs_comm:Disconnect()
  2255. if ( not(status) ) then
  2256. stdnse.debug4("rpc.Helper.ExportStats: %s", fhandle)
  2257. return status, fhandle
  2258. end
  2259. return true, stats
  2260. end,
  2261. --- Retrieves a list of files from the NFS export
  2262. --
  2263. -- @param host table
  2264. -- @param port table
  2265. -- @param path string containing the nfs export path
  2266. -- @return status true on success, false on failure
  2267. -- @return table of file table entries as described in <code>decodeReadDir</code>
  2268. Dir = function( host, port, path )
  2269. local fhandle
  2270. local dirs, status, result
  2271. local mountd, nfsd = {}, {}
  2272. local mnt_comm, nfs_comm
  2273. local mnt, nfs = Mount:new(), NFS:new()
  2274. status, mountd = Helper.GetProgramInfo( host, port, "mountd")
  2275. if ( not(status) ) then
  2276. stdnse.debug4("rpc.Helper.Dir: GetProgramInfo failed")
  2277. return status, "rpc.Helper.Dir: GetProgramInfo failed"
  2278. end
  2279. status, nfsd = Helper.GetProgramInfo( host, port, "nfs")
  2280. if ( not(status) ) then
  2281. stdnse.debug4("rpc.Helper.Dir: GetProgramInfo failed")
  2282. return status, "rpc.Helper.Dir: GetProgramInfo failed"
  2283. end
  2284. mnt_comm = Comm:new('mountd', mountd.version)
  2285. nfs_comm = Comm:new('nfs', nfsd.version)
  2286. -- TODO: recheck the version mismatch when adding NFSv4
  2287. if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
  2288. stdnse.debug4("rpc.Helper.Dir: versions mismatch, nfs v%d - mount v%d",
  2289. nfs_comm.version, mnt_comm.version)
  2290. return false, string.format("versions mismatch, nfs v%d - mount v%d",
  2291. nfs_comm.version, mnt_comm.version)
  2292. end
  2293. status, result = mnt_comm:Connect(host, mountd.port)
  2294. if ( not(status) ) then
  2295. stdnse.debug4("rpc.Helper.Dir: %s", result)
  2296. return status, result
  2297. end
  2298. status, result = nfs_comm:Connect(host, nfsd.port)
  2299. if ( not(status) ) then
  2300. mnt_comm:Disconnect()
  2301. stdnse.debug4("rpc.Helper.Dir: %s", result)
  2302. return status, result
  2303. end
  2304. status, fhandle = mnt:Mount(mnt_comm, path )
  2305. if ( not(status) ) then
  2306. mnt_comm:Disconnect()
  2307. nfs_comm:Disconnect()
  2308. stdnse.debug4("rpc.Helper.Dir: %s", fhandle)
  2309. return status, fhandle
  2310. end
  2311. status, dirs = nfs:ReadDir(nfs_comm, fhandle )
  2312. if ( not(status) ) then
  2313. mnt_comm:Disconnect()
  2314. nfs_comm:Disconnect()
  2315. stdnse.debug4("rpc.Helper.Dir: %s", dirs)
  2316. return status, dirs
  2317. end
  2318. status, fhandle = mnt:Unmount(mnt_comm, path)
  2319. mnt_comm:Disconnect()
  2320. nfs_comm:Disconnect()
  2321. if ( not(status) ) then
  2322. stdnse.debug4("rpc.Helper.Dir: %s", fhandle)
  2323. return status, fhandle
  2324. end
  2325. return true, dirs
  2326. end,
  2327. --- Retrieves NFS Attributes
  2328. --
  2329. -- @param host table
  2330. -- @param port table
  2331. -- @param path string containing the nfs export path
  2332. -- @return status true on success, false on failure
  2333. -- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
  2334. -- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
  2335. GetAttributes = function( host, port, path )
  2336. local fhandle
  2337. local attribs, status, result
  2338. local mnt_comm, nfs_comm
  2339. local mountd, nfsd = {}, {}
  2340. local mnt, nfs = Mount:new(), NFS:new()
  2341. status, mountd = Helper.GetProgramInfo( host, port, "mountd")
  2342. if ( not(status) ) then
  2343. stdnse.debug4("rpc.Helper.GetAttributes: GetProgramInfo failed")
  2344. return status, "rpc.Helper.GetAttributes: GetProgramInfo failed"
  2345. end
  2346. status, nfsd = Helper.GetProgramInfo( host, port, "nfs")
  2347. if ( not(status) ) then
  2348. stdnse.debug4("rpc.Helper.GetAttributes: GetProgramInfo failed")
  2349. return status, "rpc.Helper.GetAttributes: GetProgramInfo failed"
  2350. end
  2351. mnt_comm, result = Comm:new('mountd', mountd.version)
  2352. nfs_comm, result = Comm:new('nfs', nfsd.version)
  2353. -- TODO: recheck the version mismatch when adding NFSv4
  2354. if (nfs_comm.version <= 2 and mnt_comm.version > 2) then
  2355. stdnse.debug4("rpc.Helper.GetAttributes: versions mismatch, nfs v%d - mount v%d",
  2356. nfs_comm.version, mnt_comm.version)
  2357. return false, string.format("versions mismatch, nfs v%d - mount v%d",
  2358. nfs_comm.version, mnt_comm.version)
  2359. end
  2360. status, result = mnt_comm:Connect(host, mountd.port)
  2361. if ( not(status) ) then
  2362. stdnse.debug4("rpc.Helper.GetAttributes: %s", result)
  2363. return status, result
  2364. end
  2365. status, result = nfs_comm:Connect(host, nfsd.port)
  2366. if ( not(status) ) then
  2367. mnt_comm:Disconnect()
  2368. stdnse.debug4("rpc.Helper.GetAttributes: %s", result)
  2369. return status, result
  2370. end
  2371. status, fhandle = mnt:Mount(mnt_comm, path)
  2372. if ( not(status) ) then
  2373. mnt_comm:Disconnect()
  2374. nfs_comm:Disconnect()
  2375. stdnse.debug4("rpc.Helper.GetAttributes: %s", fhandle)
  2376. return status, fhandle
  2377. end
  2378. status, attribs = nfs:GetAttr(nfs_comm, fhandle)
  2379. if ( not(status) ) then
  2380. mnt_comm:Disconnect()
  2381. nfs_comm:Disconnect()
  2382. stdnse.debug4("rpc.Helper.GetAttributes: %s", attribs)
  2383. return status, attribs
  2384. end
  2385. status, fhandle = mnt:Unmount(mnt_comm, path)
  2386. mnt_comm:Disconnect()
  2387. nfs_comm:Disconnect()
  2388. if ( not(status) ) then
  2389. stdnse.debug4("rpc.Helper.GetAttributes: %s", fhandle)
  2390. return status, fhandle
  2391. end
  2392. return true, attribs
  2393. end,
  2394. --- Queries the portmapper for a list of programs
  2395. --
  2396. -- @param host table
  2397. -- @param port table
  2398. -- @return status true on success, false on failure
  2399. -- @return table containing the portmapper information as returned by
  2400. -- <code>Portmap.Dump</code>
  2401. RpcInfo = function( host, port )
  2402. local status, result
  2403. local portmap = Portmap:new()
  2404. local comm = Comm:new('rpcbind', 2)
  2405. mutex "lock"
  2406. if nmap.registry[host.ip] == nil then
  2407. nmap.registry[host.ip] = {}
  2408. end
  2409. if nmap.registry[host.ip]['portmapper'] == nil then
  2410. nmap.registry[host.ip]['portmapper'] = {}
  2411. elseif next(nmap.registry[host.ip]['portmapper']) ~= nil then
  2412. mutex "done"
  2413. return true, nmap.registry[host.ip]['portmapper']
  2414. end
  2415. status, result = comm:Connect(host, port)
  2416. if (not(status)) then
  2417. mutex "done"
  2418. stdnse.debug4("rpc.Helper.RpcInfo: %s", result)
  2419. return status, result
  2420. end
  2421. status, result = portmap:Dump(comm)
  2422. comm:Disconnect()
  2423. mutex "done"
  2424. if (not(status)) then
  2425. stdnse.debug4("rpc.Helper.RpcInfo: %s", result)
  2426. end
  2427. return status, result
  2428. end,
  2429. --- Queries the portmapper for a port for the specified RPC program
  2430. --
  2431. -- @param host table
  2432. -- @param port table
  2433. -- @param program string containing the RPC program name
  2434. -- @param protocol string containing either "tcp" or "udp"
  2435. -- @return status true on success, false on failure
  2436. -- @return table containing the portmapper information as returned by
  2437. -- <code>Portmap.Dump</code>
  2438. GetPortForProgram = function( host, port, program, protocol )
  2439. local status, result
  2440. local portmap = Portmap:new()
  2441. local comm = Comm:new('rpcbind', 2)
  2442. status, result = comm:Connect(host, port)
  2443. if (not(status)) then
  2444. stdnse.debug4("rpc.Helper.GetPortForProgram: %s", result)
  2445. return status, result
  2446. end
  2447. status, result = portmap:GetPort(comm, program, protocol, 1 )
  2448. comm:Disconnect()
  2449. if (not(status)) then
  2450. stdnse.debug4("rpc.Helper.GetPortForProgram: %s", result)
  2451. end
  2452. return status, result
  2453. end,
  2454. --- Get RPC program information
  2455. --
  2456. -- @param host table
  2457. -- @param port table
  2458. -- @param program string containing the RPC program name
  2459. -- @param max_version (optional) number containing highest version to retrieve
  2460. -- @return status true on success, false on failure
  2461. -- @return info table containing <code>port</code>, <code>port.number</code>
  2462. -- <code>port.protocol</code> and <code>version</code>
  2463. GetProgramInfo = function( host, port, program, max_version )
  2464. local status, portmap_table = Helper.RpcInfo(host, port)
  2465. if ( not(status) ) then
  2466. return status, portmap_table
  2467. end
  2468. local info = {}
  2469. -- assume failure
  2470. status = false
  2471. for _, p in ipairs( RPC_PROTOCOLS ) do
  2472. local tmp = portmap_table[Util.ProgNameToNumber(program)]
  2473. if ( tmp and tmp[p] ) then
  2474. info = {}
  2475. info.port = {}
  2476. info.port.number = tmp[p].port
  2477. info.port.protocol = p
  2478. -- choose the highest version available
  2479. if ( not(RPC_version[program]) ) then
  2480. info.version = tmp[p].version[#tmp[p].version]
  2481. status = true
  2482. else
  2483. for i=#tmp[p].version, 1, -1 do
  2484. if ( RPC_version[program].max >= tmp[p].version[i] ) then
  2485. if ( not(max_version) ) then
  2486. info.version = tmp[p].version[i]
  2487. status = true
  2488. break
  2489. else
  2490. if ( max_version >= tmp[p].version[i] ) then
  2491. info.version = tmp[p].version[i]
  2492. status = true
  2493. break
  2494. end
  2495. end
  2496. end
  2497. end
  2498. end
  2499. break
  2500. end
  2501. end
  2502. return status, info
  2503. end,
  2504. }
  2505. --- Static class containing mostly conversion functions
  2506. -- and File type codes and permissions emulation
  2507. Util =
  2508. {
  2509. -- Symbolic letters for file permission codes
  2510. Fperm =
  2511. {
  2512. owner =
  2513. {
  2514. -- S_IRUSR
  2515. [0x00000100] = { idx = 1, char = "r" },
  2516. -- S_IWUSR
  2517. [0x00000080] = { idx = 2, char = "w" },
  2518. -- S_IXUSR
  2519. [0x00000040] = { idx = 3, char = "x" },
  2520. -- S_ISUID
  2521. [0x00000800] = { idx = 3, char = "S" },
  2522. },
  2523. group =
  2524. {
  2525. -- S_IRGRP
  2526. [0x00000020] = { idx = 4, char = "r" },
  2527. -- S_IWGRP
  2528. [0x00000010] = { idx = 5, char = "w" },
  2529. -- S_IXGRP
  2530. [0x00000008] = { idx = 6, char = "x" },
  2531. -- S_ISGID
  2532. [0x00000400] = { idx = 6, char = "S" },
  2533. },
  2534. other =
  2535. {
  2536. -- S_IROTH
  2537. [0x00000004] = { idx = 7, char = "r" },
  2538. -- S_IWOTH
  2539. [0x00000002] = { idx = 8, char = "w" },
  2540. -- S_IXOTH
  2541. [0x00000001] = { idx = 9, char = "x" },
  2542. -- S_ISVTX
  2543. [0x00000200] = { idx = 9, char = "t" },
  2544. },
  2545. },
  2546. -- bit mask used to extract the file type code from a mode
  2547. -- S_IFMT = 00170000 (octal)
  2548. S_IFMT = 0xF000,
  2549. FileType =
  2550. {
  2551. -- S_IFSOCK
  2552. [0x0000C000] = { char = "s", str = "socket" },
  2553. -- S_IFLNK
  2554. [0x0000A000] = { char = "l", str = "symbolic link" },
  2555. -- S_IFREG
  2556. [0x00008000] = { char = "-", str = "file" },
  2557. -- S_IFBLK
  2558. [0x00006000] = { char = "b", str = "block device" },
  2559. -- S_IFDIR
  2560. [0x00004000] = { char = "d", str = "directory" },
  2561. -- S_IFCHR
  2562. [0x00002000] = { char = "c", str = "char device" },
  2563. -- S_IFIFO
  2564. [0x00001000] = { char = "p", str = "named pipe" },
  2565. },
  2566. --- Converts a numeric ACL mode to a file type char
  2567. --
  2568. -- @param mode number containing the ACL mode
  2569. -- @return char containing the file type
  2570. FtypeToChar = function(mode)
  2571. local code = bit.band(mode, Util.S_IFMT)
  2572. if Util.FileType[code] then
  2573. return Util.FileType[code].char
  2574. else
  2575. stdnse.debug1("FtypeToChar: Unknown file type, mode: %o", mode)
  2576. return ""
  2577. end
  2578. end,
  2579. --- Converts a numeric ACL mode to a file type string
  2580. --
  2581. -- @param mode number containing the ACL mode
  2582. -- @return string containing the file type name
  2583. FtypeToString = function(mode)
  2584. local code = bit.band(mode, Util.S_IFMT)
  2585. if Util.FileType[code] then
  2586. return Util.FileType[code].str
  2587. else
  2588. stdnse.debug1("FtypeToString: Unknown file type, mode: %o", mode)
  2589. return ""
  2590. end
  2591. end,
  2592. --- Converts a numeric ACL mode to a string in an octal
  2593. -- number format.
  2594. --
  2595. -- @param mode number containing the ACL mode
  2596. -- @return string containing the octal ACL mode
  2597. FmodeToOctalString = function(mode)
  2598. local code = bit.band(mode, Util.S_IFMT)
  2599. if Util.FileType[code] then
  2600. code = bit.bxor(mode, code)
  2601. else
  2602. code = mode
  2603. stdnse.debug1("FmodeToOctalString: Unknown file type, mode: %o", mode)
  2604. end
  2605. return stdnse.tooctal(code)
  2606. end,
  2607. --- Converts a numeric ACL to its character equivalent eg. (rwxr-xr-x)
  2608. --
  2609. -- @param mode number containing the ACL mode
  2610. -- @return string containing the ACL characters
  2611. FpermToString = function(mode)
  2612. local tmpacl = { "-", "-", "-", "-", "-", "-", "-", "-", "-" }
  2613. for user,_ in pairs(Util.Fperm) do
  2614. local t = Util.Fperm[user]
  2615. for i in pairs(t) do
  2616. local code = bit.band(mode, i)
  2617. if t[code] then
  2618. -- save set-ID and sticky bits
  2619. if tmpacl[t[code].idx] == "x" then
  2620. if t[code].char == "S" then
  2621. tmpacl[t[code].idx] = "s"
  2622. else
  2623. tmpacl[t[code].idx] = t[code].char
  2624. end
  2625. elseif tmpacl[t[code].idx] == "S" then
  2626. if t[code].char == "x" then
  2627. tmpacl[t[code].idx] = "s"
  2628. end
  2629. else
  2630. tmpacl[t[code].idx] = t[code].char
  2631. end
  2632. end
  2633. end
  2634. end
  2635. return table.concat(tmpacl)
  2636. end,
  2637. --- Converts the NFS file attributes to a string.
  2638. --
  2639. -- An optional second argument is the mactime to use
  2640. --
  2641. -- @param attr table returned by NFS GETATTR or ACCESS
  2642. -- @param mactime to use, the default value is mtime
  2643. -- Possible values: mtime, atime, ctime
  2644. -- @return string containing the file attributes
  2645. format_nfsfattr = function(attr, mactime)
  2646. local time = "mtime"
  2647. if mactime then
  2648. time = mactime
  2649. end
  2650. return string.format("%s%s uid: %5d gid: %5d %6s %s",
  2651. Util.FtypeToChar(attr.mode),
  2652. Util.FpermToString(attr.mode),
  2653. attr.uid,
  2654. attr.gid,
  2655. Util.SizeToHuman(attr.size),
  2656. Util.TimeToString(attr[time].seconds))
  2657. end,
  2658. marshall_int32 = function(int32, count)
  2659. if count then
  2660. return bin.pack(">i" .. count, int32)
  2661. end
  2662. return bin.pack(">i", int32)
  2663. end,
  2664. unmarshall_int32 = function(data, pos, count)
  2665. if count then
  2666. return bin.unpack(">i" .. count, data, pos)
  2667. end
  2668. return bin.unpack(">i", data, pos)
  2669. end,
  2670. marshall_uint32 = function(uint32, count)
  2671. if count then
  2672. return bin.pack(">I" .. count, uint32)
  2673. end
  2674. return bin.pack(">I", uint32)
  2675. end,
  2676. unmarshall_uint32 = function(data, pos, count)
  2677. if count then
  2678. return bin.unpack(">I" .. count, data, pos)
  2679. end
  2680. return bin.unpack(">I", data, pos)
  2681. end,
  2682. marshall_int64 = function(int64, count)
  2683. if count then
  2684. return bin.pack(">l" .. count, int64)
  2685. end
  2686. return bin.pack(">l", int64)
  2687. end,
  2688. unmarshall_int64 = function(data, pos, count)
  2689. if count then
  2690. return bin.unpack(">l" .. count, data, pos)
  2691. end
  2692. return bin.unpack(">l", data, pos)
  2693. end,
  2694. marshall_uint64 = function(uint64, count)
  2695. if count then
  2696. return bin.pack(">L" .. count, uint64)
  2697. end
  2698. return bin.pack(">L", uint64)
  2699. end,
  2700. unmarshall_uint64 = function(data, pos, count)
  2701. if count then
  2702. return bin.unpack(">L" .. count, data, pos)
  2703. end
  2704. return bin.unpack(">L", data, pos)
  2705. end,
  2706. marshall_opaque = function(data)
  2707. return bin.pack(">A", data) .. string.rep("\0", Util.CalcFillBytes(data:len()))
  2708. end,
  2709. unmarshall_opaque = function(len, data, pos)
  2710. return bin.unpack(">A" .. len, data, pos)
  2711. end,
  2712. marshall_vopaque = function(data)
  2713. local l = data:len()
  2714. return (
  2715. Util.marshall_uint32(l) .. bin.pack(">A", data) ..
  2716. string.rep("\0", Util.CalcFillBytes(l))
  2717. )
  2718. end,
  2719. unmarshall_vopaque = function(len, data, pos)
  2720. local opaque, pad
  2721. pad = Util.CalcFillBytes(len)
  2722. pos, opaque = bin.unpack(">A" .. len, data, pos)
  2723. return pos + pad, opaque
  2724. end,
  2725. unmarshall_nfsftype = function(data, pos, count)
  2726. return Util.unmarshall_uint32(data, pos, count)
  2727. end,
  2728. unmarshall_nfsfmode = function(data, pos, count)
  2729. return Util.unmarshall_uint32(data, pos, count)
  2730. end,
  2731. unmarshall_nfssize3 = function(data, pos, count)
  2732. return Util.unmarshall_uint64(data, pos, count)
  2733. end,
  2734. unmarshall_nfsspecdata3 = function(data, pos)
  2735. local specdata3 = {}
  2736. pos, specdata3.specdata1,
  2737. specdata3.specdata2 = Util.unmarshall_uint32(data, pos, 2)
  2738. return pos, specdata3
  2739. end,
  2740. --- Unmarshall NFSv3 fileid field of the NFS attributes
  2741. --
  2742. -- @param data The data being processed.
  2743. -- @param pos The position within <code>data</code>
  2744. -- @return pos The new position
  2745. -- @return uint64 The decoded fileid
  2746. unmarshall_nfsfileid3 = function(data, pos)
  2747. return Util.unmarshall_uint64(data, pos)
  2748. end,
  2749. --- Unmarshall NFS time
  2750. --
  2751. -- @param data The data being processed.
  2752. -- @param pos The position within <code>data</code>
  2753. -- @return pos The new position
  2754. -- @return table The decoded NFS time table.
  2755. unmarshall_nfstime = function(data, pos)
  2756. local nfstime = {}
  2757. pos, nfstime.seconds,
  2758. nfstime.nseconds = Util.unmarshall_uint32(data, pos, 2)
  2759. return pos, nfstime
  2760. end,
  2761. --- Unmarshall NFS file attributes
  2762. --
  2763. -- @param data The data being processed.
  2764. -- @param pos The position within <code>data</code>
  2765. -- @param number The NFS version.
  2766. -- @return pos The new position
  2767. -- @return table The decoded file attributes table.
  2768. unmarshall_nfsattr = function(data, pos, nfsversion)
  2769. local attr = {}
  2770. pos, attr.type = Util.unmarshall_nfsftype(data, pos)
  2771. pos, attr.mode = Util.unmarshall_nfsfmode(data, pos)
  2772. pos, attr.nlink, attr.uid,
  2773. attr.gid = Util.unmarshall_uint32(data, pos, 3)
  2774. if (nfsversion < 3) then
  2775. pos, attr.size, attr.blocksize, attr.rdev, attr.blocks,
  2776. attr.fsid, attr.fileid = Util.unmarshall_uint32(data, pos, 6)
  2777. elseif (nfsversion == 3) then
  2778. pos, attr.size = Util.unmarshall_nfssize3(data, pos)
  2779. pos, attr.used = Util.unmarshall_nfssize3(data, pos)
  2780. pos, attr.rdev = Util.unmarshall_nfsspecdata3(data, pos)
  2781. pos, attr.fsid = Util.unmarshall_uint64(data, pos)
  2782. pos, attr.fileid = Util.unmarshall_nfsfileid3(data, pos)
  2783. else
  2784. stdnse.debug4("unmarshall_nfsattr: unsupported NFS version %d",
  2785. nfsversion)
  2786. return -1, nil
  2787. end
  2788. pos, attr.atime = Util.unmarshall_nfstime(data, pos)
  2789. pos, attr.mtime = Util.unmarshall_nfstime(data, pos)
  2790. pos, attr.ctime = Util.unmarshall_nfstime(data, pos)
  2791. return pos, attr
  2792. end,
  2793. --- Returns a string containing date and time
  2794. --
  2795. -- @param number of seconds since some given start time
  2796. -- (the "epoch")
  2797. -- @return string that represents time.
  2798. TimeToString = stdnse.format_timestamp,
  2799. --- Converts the size in bytes to a human readable format
  2800. --
  2801. -- An optional second argument is the size of a block
  2802. -- @usage
  2803. -- size_tohuman(1024) --> 1024.0B
  2804. -- size_tohuman(926548776) --> 883.6M
  2805. -- size_tohuman(246548, 1024) --> 240.8K
  2806. -- size_tohuman(246548, 1000) --> 246.5K
  2807. --
  2808. -- @param size in bytes
  2809. -- @param blocksize represents the number of bytes per block
  2810. -- Possible values are: 1024 or 1000
  2811. -- Default value is: 1024
  2812. -- @return string containing the size in the human readable
  2813. -- format
  2814. SizeToHuman = function(size, blocksize)
  2815. local bs, idx = 1024, 1
  2816. local unit = { "B", "K", "M", "G" , "T"}
  2817. if blocksize and blocksize == 1000 then
  2818. bs = blocksize
  2819. end
  2820. for i=1, #unit do
  2821. if (size > bs and idx < #unit) then
  2822. size = size / bs
  2823. idx = idx + 1
  2824. end
  2825. end
  2826. return string.format("%.1f%s", size, unit[idx])
  2827. end,
  2828. format_access = function(mask, version)
  2829. local ret, nfsobj = "", NFS:new()
  2830. if nfsobj:AccessRead(mask, version) ~= 0 then
  2831. ret = "Read "
  2832. else
  2833. ret = "NoRead "
  2834. end
  2835. if nfsobj:AccessLookup(mask, version) ~= 0 then
  2836. ret = ret .. "Lookup "
  2837. else
  2838. ret = ret .. "NoLookup "
  2839. end
  2840. if nfsobj:AccessModify(mask, version) ~= 0 then
  2841. ret = ret .. "Modify "
  2842. else
  2843. ret = ret .. "NoModify "
  2844. end
  2845. if nfsobj:AccessExtend(mask, version) ~= 0 then
  2846. ret = ret .. "Extend "
  2847. else
  2848. ret = ret .. "NoExtend "
  2849. end
  2850. if nfsobj:AccessDelete(mask, version) ~= 0 then
  2851. ret = ret .. "Delete "
  2852. else
  2853. ret = ret .. "NoDelete "
  2854. end
  2855. if nfsobj:AccessExecute(mask, version) ~= 0 then
  2856. ret = ret .. "Execute"
  2857. else
  2858. ret = ret .. "NoExecute"
  2859. end
  2860. return ret
  2861. end,
  2862. --- Return the pathconf filesystem table
  2863. --
  2864. -- @param pconf table returned by the NFSv3 PATHCONF call
  2865. -- @param nfsversion the version of the remote NFS server
  2866. -- @return fs table that contains the remote filesystem
  2867. -- pathconf information.
  2868. calc_pathconf_table = function(pconf, nfsversion)
  2869. local fs = {}
  2870. if nfsversion ~= 3 then
  2871. return nil, "ERROR: unsupported NFS version."
  2872. end
  2873. fs.linkmax = pconf.linkmax
  2874. fs.name_max = pconf.name_max
  2875. if pconf.chown_restricted then
  2876. fs.chown_restricted = "True"
  2877. else
  2878. fs.chown_restricted = "False"
  2879. end
  2880. return fs, nil
  2881. end,
  2882. --- Calculate and return the fsinfo filesystem table
  2883. --
  2884. -- @param fsinfo table returned by the NFSv3 FSINFO call
  2885. -- @param nfsversion the version of the remote NFS server
  2886. -- @param human if set show the size in the human
  2887. -- readable format.
  2888. -- @return fs table that contains the remote filesystem
  2889. -- information.
  2890. calc_fsinfo_table = function(fsinfo, nfsversion, human)
  2891. local fs = {}
  2892. local nfsobj = NFS:new()
  2893. if nfsversion ~= 3 then
  2894. return nil, "ERROR: unsupported NFS version."
  2895. end
  2896. fs.maxfilesize = Util.SizeToHuman(fsinfo.maxfilesize)
  2897. if nfsobj:FSinfoLink(fsinfo.properties, nfsversion) ~= 0 then
  2898. fs.link = "True"
  2899. else
  2900. fs.link = "False"
  2901. end
  2902. if nfsobj:FSinfoSymlink(fsinfo.properties, nfsversion) ~= 0 then
  2903. fs.symlink = "True"
  2904. else
  2905. fs.symlink = "False"
  2906. end
  2907. return fs, nil
  2908. end,
  2909. --- Calculate and return the fsstat filesystem table
  2910. --
  2911. -- @param stats table returned by the NFSv3 FSSTAT or
  2912. -- NFSv2 STATFS calls
  2913. -- @param nfsversion the version of the remote NFS server
  2914. -- @param human if set show the size in the human
  2915. -- readable format.
  2916. -- @return df table that contains the remote filesystem
  2917. -- attributes.
  2918. calc_fsstat_table = function(stats, nfsversion, human)
  2919. local df, base = {}, 1024
  2920. local size, free, total, avail, used, use
  2921. if (nfsversion == 3) then
  2922. free = stats.fbytes
  2923. size = stats.tbytes
  2924. avail = stats.abytes
  2925. elseif (nfsversion == 2) then
  2926. df.bsize = stats.block_size
  2927. free = stats.free_blocks * df.bsize
  2928. size = stats.total_blocks * df.bsize
  2929. avail = stats.available_blocks * df.bsize
  2930. else
  2931. return nil, "ERROR: unsupported NFS version."
  2932. end
  2933. if (human) then
  2934. if (df.bsize) then
  2935. df.bsize = Util.SizeToHuman(df.bsize)
  2936. end
  2937. df.size = Util.SizeToHuman(size)
  2938. df.available = Util.SizeToHuman(avail)
  2939. used = size - free
  2940. avail = avail
  2941. df.used = Util.SizeToHuman(used)
  2942. total = used + avail
  2943. else
  2944. free = free / base
  2945. df.size = size / base
  2946. df.available = avail / base
  2947. used = df.size - free
  2948. df.used = used
  2949. total = df.used + df.available
  2950. end
  2951. use = math.ceil(used * 100 / total)
  2952. df.use = string.format("%.0f%%", use)
  2953. return df, nil
  2954. end,
  2955. --- Converts a RPC program name to its equivalent number
  2956. --
  2957. -- @param prog_name string containing the name of the RPC program
  2958. -- @return num number containing the program ID
  2959. ProgNameToNumber = function(prog_name)
  2960. local status
  2961. if not( RPC_PROGRAMS ) then
  2962. status, RPC_PROGRAMS = datafiles.parse_rpc()
  2963. if ( not(status) ) then
  2964. return
  2965. end
  2966. end
  2967. for num, name in pairs(RPC_PROGRAMS) do
  2968. if ( prog_name == name ) then
  2969. return num
  2970. end
  2971. end
  2972. return
  2973. end,
  2974. --- Converts the RPC program number to its equivalent name
  2975. --
  2976. -- @param num number containing the RPC program identifier
  2977. -- @return string containing the RPC program name
  2978. ProgNumberToName = function( num )
  2979. local status
  2980. if not( RPC_PROGRAMS ) then
  2981. status, RPC_PROGRAMS = datafiles.parse_rpc()
  2982. if ( not(status) ) then
  2983. return
  2984. end
  2985. end
  2986. return RPC_PROGRAMS[num]
  2987. end,
  2988. --
  2989. -- Calculates the number of fill bytes needed
  2990. -- @param length contains the length of the string
  2991. -- @return the amount of pad needed to be dividable by 4
  2992. CalcFillBytes = function(length)
  2993. -- calculate fill bytes
  2994. if math.fmod( length, 4 ) ~= 0 then
  2995. return (4 - math.fmod( length, 4))
  2996. else
  2997. return 0
  2998. end
  2999. end
  3000. }
  3001. return _ENV;