diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:42:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:42:04 +0000 |
commit | 0d47952611198ef6b1163f366dc03922d20b1475 (patch) | |
tree | 3d840a3b8c0daef0754707bfb9f5e873b6b1ac13 /nselib/xdmcp.lua | |
parent | Initial commit. (diff) | |
download | nmap-0d47952611198ef6b1163f366dc03922d20b1475.tar.xz nmap-0d47952611198ef6b1163f366dc03922d20b1475.zip |
Adding upstream version 7.94+git20230807.3be01efb1+dfsg.upstream/7.94+git20230807.3be01efb1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nselib/xdmcp.lua')
-rw-r--r-- | nselib/xdmcp.lua | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/nselib/xdmcp.lua b/nselib/xdmcp.lua new file mode 100644 index 0000000..30bf6c3 --- /dev/null +++ b/nselib/xdmcp.lua @@ -0,0 +1,407 @@ +--- +-- Implementation of the XDMCP (X Display Manager Control Protocol) based on: +-- x http://www.xfree86.org/current/xdmcp.pdf +-- +-- @author Patrik Karlsson <patrik@cqure.net> + +local ipOps = require "ipOps" +local nmap = require "nmap" +local stdnse = require "stdnse" +local string = require "string" +local table = require "table" +_ENV = stdnse.module("xdmcp", stdnse.seeall) + +-- Supported operations +OpCode = { + BCAST_QUERY = 1, + QUERY = 2, + WILLING = 5, + REQUEST = 7, + ACCEPT = 8, + MANAGE = 10, +} + +-- Packet class +Packet = { + + -- The cdmcp header + Header = { + + -- Creates a new instance of class + -- @param version number containing the protocol version + -- @param opcode number containing the opcode type + -- @param length number containing the length of the data + -- @return o instance of class + new = function(self, version, opcode, length) + local o = { version = version, opcode = opcode, length = length } + setmetatable(o, self) + self.__index = self + return o + end, + + -- Parses data based on which a new object is instantiated + -- @param data opaque string containing data received over the wire + -- @return hdr instance of class + -- @return pos position in the string where parsing left off + parse = function(data) + local hdr = Packet.Header:new() + local pos + hdr.version, hdr.opcode, hdr.length, pos = string.unpack(">I2I2I2", data) + return hdr, pos + end, + + -- Converts the instance to an opaque string + -- @return str string containing the instance + __tostring = function(self) + assert(self.length, "No header length was supplied") + return string.pack(">I2I2I2", self.version, self.opcode, self.length) + end, + }, + + [OpCode.QUERY] = { + + -- Creates a new instance of class + -- @param authnames table of strings containing authentication + -- mechanism names. + -- @return o instance of class + new = function(self, authnames) + local o = { + header = Packet.Header:new(1, OpCode.QUERY), + authnames = authnames or {}, + } + o.header.length = #o.authnames + 1 + setmetatable(o, self) + self.__index = self + return o + end, + + -- Converts the instance to an opaque string + -- @return str string containing the instance + __tostring = function(self) + local data = { + tostring(self.header), + string.pack("B", #self.authnames), + } + for _, name in ipairs(self.authnames) do + data[#data+1] = string.pack(">s2", name) + end + return table.concat(data) + end, + + }, + + [OpCode.BCAST_QUERY] = { + new = function(...) + local packet = Packet[OpCode.QUERY]:new(...) + packet.header.opcode = OpCode.BCAST_QUERY + return packet + end, + + __tostring = function(...) + return Packet[OpCode.QUERY]:__tostring(...) + end + + }, + + [OpCode.WILLING] = { + + -- Creates a new instance of class + -- @return o instance of class + new = function(self) + local o = { + header = Packet.Header:new(1, OpCode.WILLING) + } + setmetatable(o, self) + self.__index = self + return o + end, + + -- Parses data based on which a new object is instantiated + -- @param data opaque string containing data received over the wire + -- @return hdr instance of class + parse = function(data) + local willing = Packet[OpCode.WILLING]:new() + local pos + willing.header, pos = Packet.Header.parse(data) + + willing.authname, willing.hostname, + willing.status, pos = string.unpack("s1s1s1", data, pos) + return willing + end, + + }, + + [OpCode.REQUEST] = { + + -- The connection class + Connection = { + + IpType = { + IPv4 = 0, + IPv6 = 6, + }, + + -- Creates a new instance of class + -- @param iptype number + -- @param ip opaque string containing the ip + -- @return o instance of class + new = function(self, iptype, ip) + local o = { + iptype = iptype, + ip = ip, + } + setmetatable(o, self) + self.__index = self + return o + end, + + }, + + -- Creates a new instance of class + -- @param disp_no number containing the display name + -- @param auth_name string containing the authentication name + -- @param auth_data string containing additional authentication data + -- @param authr_names string containing authorization mechanisms + -- @param manf_id string containing the manufacturer id + -- @return o instance of class + new = function(self, disp_no, conns, auth_name, auth_data, authr_names, manf_id ) + local o = { + header = Packet.Header:new(1, OpCode.REQUEST), + disp_no = disp_no or 1, + conns = conns or {}, + auth_name = auth_name or "", + auth_data = auth_data or "", + authr_names = authr_names or {}, + manf_id = manf_id or "", + } + setmetatable(o, self) + self.__index = self + return o + end, + + -- Adds a new connection entry + -- @param conn instance of Connections + addConnection = function(self, conn) + table.insert(self.conns, conn) + end, + + -- Adds a new authorization entry + -- @param str string containing the name of the authorization mechanism + addAuthrName = function(self, str) + table.insert(self.authr_names, str) + end, + + -- Converts the instance to an opaque string + -- @return str string containing the instance + __tostring = function(self) + local data = { + string.pack(">I2B", self.disp_no, #self.conns), + } + for _, conn in ipairs(self.conns) do + data[#data+1] = string.pack(">I2", conn.iptype) + end + data[#data+1] = string.pack("B", #self.conns) + for _, conn in ipairs(self.conns) do + data[#data+1] = string.pack(">s2", ipOps.ip_to_str(conn.ip)) + end + data[#data+1] = string.pack(">s2s2B", self.auth_name, self.auth_data, #self.authr_names) + for _, authr in ipairs(self.authr_names) do + data[#data+1] = string.pack(">s2", authr) + end + data[#data+1] = string.pack(">s2", self.manf_id) + data = table.concat(data) + self.header.length = #data + + return tostring(self.header) .. data + end, + + }, + + [OpCode.ACCEPT] = { + + -- Creates a new instance of class + -- @param session_id number containing the session id + -- @param auth_name string containing the authentication name + -- @param auth_data string containing additional authentication data + -- @param authr_name string containing the authorization mechanism name + -- @param authr_names string containing authorization mechanisms + -- @return o instance of class + new = function(self, session_id, auth_name, auth_data, authr_name, authr_data) + local o = { + header = Packet.Header:new(1, OpCode.ACCEPT), + session_id = session_id, + auth_name = auth_name, + auth_data = auth_data, + authr_name = authr_name, + authr_data = authr_data, + } + setmetatable(o, self) + self.__index = self + return o + end, + + -- Parses data based on which a new object is instantiated + -- @param data opaque string containing data received over the wire + -- @return hdr instance of class + parse = function(data) + local accept = Packet[OpCode.ACCEPT]:new() + local pos + accept.header, pos = Packet.Header.parse(data) + accept.session_id, accept.auth_name, accept.auth_data, + accept.authr_name, accept.authr_data, pos = string.unpack(">I4s2s2s2s2", data, pos) + return accept + end, + + }, + + [OpCode.MANAGE] = { + + -- Creates a new instance of class + -- @param session_id number containing the session id + -- @param disp_no number containing the display number + -- @param disp_class string containing the display class + -- @return o instance of class + new = function(self, sess_id, disp_no, disp_class) + local o = { + header = Packet.Header:new(1, OpCode.MANAGE), + session_id = sess_id, + disp_no = disp_no, + disp_class = disp_class or "" + } + setmetatable(o, self) + self.__index = self + return o + end, + + -- Converts the instance to an opaque string + -- @return str string containing the instance + __tostring = function(self) + local data = string.pack(">I4I2s2", self.session_id, self.disp_no, self.disp_class) + self.header.length = #data + return tostring(self.header) .. data + end, + + } + +} + +-- The Helper class serves as the main script interface +Helper = { + + -- Creates a new instance of Helper + -- @param host table as received by the action method + -- @param port table as received by the action method + -- @param options table + -- @return o new instance of Helper + new = function(self, host, port, options) + local o = { + host = host, + port = port, + options = options or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, + + -- "Connects" to the server (ie. creates the socket) + -- @return status, true on success, false on failure + connect = function(self) + self.socket = nmap.new_socket("udp") + self.socket:set_timeout(self.options.timeout or 10000) + return true + end, + + -- Creates a xdmcp session + -- @param auth_name string containing the authentication name + -- @param authr_name string containing the authorization mechanism name + -- @param disp_class string containing the display class + -- @return status true on success, false on failure + -- @return response table or err string containing an error message + createSession = function(self, auth_names, authr_names, disp_no) + local info = nmap.get_interface_info(self.host.interface) + if ( not(info) ) then + return false, ("Failed to get information for interface %s"):format(self.host.interface) + end + + local req = Packet[OpCode.QUERY]:new(auth_names) + local status, response = self:exch(req) + if ( not(status) ) then + return false, response + elseif ( response.header.opcode ~= OpCode.WILLING ) then + return false, "Received unexpected response" + end + + local REQ = Packet[OpCode.REQUEST] + local iptype = REQ.Connection.IpType.IPv4 + if ( nmap.address_family() == 'inet6' ) then + iptype = REQ.Connection.IpType.IPv6 + end + + local conns = { REQ.Connection:new(iptype, info.address) } + local req = REQ:new(disp_no, conns, nil, nil, authr_names) + local status, response = self:exch(req) + if ( not(status) ) then + return false, response + elseif ( response.header.opcode ~= OpCode.ACCEPT ) then + return false, "Received unexpected response" + end + + -- Sending this last manage packet doesn't make any sense as we can't + -- set up a listening TCP server anyway. When we can, we could enable + -- this and wait for the incoming request and retrieve X protocol info. + + -- local manage = Packet[OpCode.MANAGE]:new(response.session_id, + -- disp_no, "MIT-unspecified") + -- local status, response = self:exch(manage) + -- if ( not(status) ) then + -- return false, response + -- end + + return true, { + session_id = response.session_id, + auth_name = response.auth_name, + auth_data = response.auth_data, + authr_name = response.authr_name, + authr_data = response.authr_data, + } + end, + + send = function(self, req) + return self.socket:sendto(self.host, self.port, tostring(req)) + end, + + recv = function(self) + local status, data = self.socket:receive() + if ( not(status) ) then + return false, data + end + local header = Packet.Header.parse(data) + if ( not(header) ) then + return false, "Failed to parse xdmcp header" + end + if ( not(Packet[header.opcode]) ) then + return false, ("No parser for opcode: %d"):format(header.opcode) + end + local resp = Packet[header.opcode].parse(data) + if ( not(resp) ) then + return false, "Failed to parse response" + end + return true, resp + end, + + -- Sends a request to the server, receives and parses a response + -- @param req instance of Packet + -- @return status true on success, false on failure + -- @return response instance of response packet + exch = function(self, req) + local status, err = self:send(req) + if ( not(status) ) then + return false, "Failed to send xdmcp request" + end + return self:recv() + end, + +} + +return _ENV; |