--- A minimalistic library to support Domino RPC
--
-- Summary
-- -------
-- The library currently only supports user enumeration and uses chunks of
-- captured data to do so.
--
-- Overview
-- --------
-- The library contains the following classes:
--
-- o DominoPacket
-- - The packet class holding the packets sent between the client and the
-- IBM Lotus Domino server
--
-- o Helper
-- - A helper class that provides easy access to the rest of the library
--
-- o DominoSocket
-- - This is a copy of the DB2Socket class which provides fundamental
-- buffering
--
--
-- Example
-- -------
-- The following sample code illustrates how scripts can use the Helper class
-- to interface the library:
--
--
-- helper = nrpc.Helper:new(host, port)
-- status, err = nrpc:Connect()
-- status, res = nrpc:isValidUser("Patrik Karlsson")
-- status, err = nrpc:Close()
--
--
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
-- @author "Patrik Karlsson "
--
--
-- Version 0.1
-- Created 07/23/2010 - v0.1 - created by Patrik Karlsson
--
local bin = require "bin"
local nmap = require "nmap"
local stdnse = require "stdnse"
_ENV = stdnse.module("nrpc", stdnse.seeall)
-- The Domino Packet
DominoPacket = {
--- Creates a new DominoPacket instance
--
-- @param data string containing the packet data
-- @return a new DominoPacket instance
new = function( self, data )
local o = {}
setmetatable(o, self)
self.__index = self
o.data = data
return o
end,
--- Reads a packet from the DominoSocket
--
-- @param domsock DominoSocket connected to the server
-- @return Status (true or false).
-- @return Error code (if status is false).
read = function( self, domsock )
local status, data = domsock:recv(2)
local pos, len = bin.unpack( ""tcp", "udp"
, or
-- @return Status (true or false).
-- @return Error code (if status is false).
connect = function( self, hostid, port, protocol )
self.Socket:set_timeout(5000)
return self.Socket:connect( hostid, port, protocol )
end,
--- Closes an open connection.
--
-- @return Status (true or false).
-- @return Error code (if status is false).
close = function( self )
return self.Socket:close()
end,
--- Opposed to the socket:receive_bytes
function, that returns
-- at least x bytes, this function returns the amount of bytes requested.
--
-- @param count of bytes to read
-- @return true on success, false on failure
-- @return data containing bytes read from the socket
-- err containing error message if status is false
recv = function( self, count )
local status, data
self.Buffer = self.Buffer or ""
if ( #self.Buffer < count ) then
status, data = self.Socket:receive_bytes( count - #self.Buffer )
if ( not(status) or #data < count - #self.Buffer ) then
return false, data
end
self.Buffer = self.Buffer .. data
end
data = self.Buffer:sub( 1, count )
self.Buffer = self.Buffer:sub( count + 1)
return true, data
end,
--- Sends data over the socket
--
-- @return Status (true or false).
-- @return Error code (if status is false).
send = function( self, data )
return self.Socket:send( data )
end,
}
Helper = {
--- Creates a new Helper instance
--
-- @param host table as recieved by the script action method
-- @param port table as recieved by the script action method
new = function(self, host, port)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = host
o.port = port
o.domsock = DominoSocket:new()
return o
end,
--- Connects the socket to the Domino server
--
-- @return status true on success, false on failure
-- @return err error message if status is false
connect = function( self )
if( not( self.domsock:connect( self.host.ip, self.port.number, "tcp" ) ) ) then
return false, ("ERROR: Failed to connect to Domino server %s:%d\n"):format(self.host, self.port)
end
return true
end,
--- Disconnects from the Lotus Domino Server
--
-- @return status true on success, false on failure
-- @return err error message if status is false
disconnect = function( self )
return self.domsock:close()
end,
--- Attempt to check whether the user exists in Domino or not
--
-- @param username string containing the user name to guess
-- @return status true on success false on failure
-- @return domino_id if it exists and status is true
-- err if status is false
isValidUser = function( self, username )
local data = bin.pack("H", "00001e00000001000080000007320000700104020000fb2b2d00281f1e000000124c010000000000")
local status, id_data
local data_len, pos, total_len, pkt_type, valid_user
self.domsock:send( tostring(DominoPacket:new( data )) )
data = DominoPacket:new():read( self.domsock )
data = bin.pack("HCHAH", "0100320002004f000100000500000900", #username + 1, "000000000000000000000000000000000028245573657273290000", username, "00")
self.domsock:send( tostring(DominoPacket:new( data ) ) )
status, id_data = DominoPacket:new():read( self.domsock )
pos, pkt_type = bin.unpack("C", id_data, 3)
pos, valid_user = bin.unpack("C", id_data, 11)
pos, total_len = bin.unpack("