PageRenderTime 71ms CodeModel.GetById 25ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full file

  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_hand

Large files files are truncated, but you can click here to view the full file