local nmap = require "nmap"
local stdnse = require "stdnse"
local shortport = require "shortport"
local table = require "table"
local ike = require "ike"


description=[[
	Get information from an IKE service.
	Tests the service with both Main and Aggressive Mode. 
	Extracts ID info (if aggressive mode is enabled), and 
	vendor IDs
]]


---
-- @output
-- PORT    STATE SERVICE REASON       VERSION
-- 500/udp open  isakmp  udp-response SonicWall
-- | ike-info: 
-- | Information
-- |   ID details
-- |     Type
-- |       ID_USER_FQDN
-- |     Value
-- |       DJ-G005
-- |   Vendor IDs
-- |_    SonicWall 


author = "Jesper Kueckelhahn"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}

portrule = shortport.port_or_service(500, "isakmp", "udp")

-- Send the ike packet and return response
local function send_ike_request(host, port, mode, transforms, diffie, id)
	stdnse.print_debug(1, "Sending %s mode packet ...", mode)
	packet = ike.request(port.number, "udp", mode, transforms, diffie, id)
	version = ike.send_request(host, port, packet)
	if version.success then
		return version
	end
	stdnse.print_debug(1, "%s mode (dh %s) failed", mode, diffie)
	return 
end

-- Generate the transforms
local function generate_transforms(auth, encryption, hash, group)
	t = {}
	for _,a in pairs(auth) do
		for _,e in pairs(encryption) do
			for _,h in pairs(hash) do
				for _,g in pairs(group) do
					table.insert(t, { ['auth'] = 'psk', ['encryption'] = e, ['hash'] = h, ['group'] = g});
				end
			end
		end
	end
	return t
end



-- Test different methods for getting a response from the service 
-- 
local function get_version(host, port)
	local packet, version, t 
	

	--------------------------------------------------------------------------------
	-- Aggressive mode
	--

	local auth			= {"psk"}
	local encryption	= {"des", "3des", "cast", "aes/128", "aes/192", "aes/256"}
	local hash			= {"md5", "sha1", "sha2-256", "sha2-384", "sha2-512"}
	local group			= {"1024"}

	-- generate aggressive mode pre-shared key transforms
	local transforms = generate_transforms(auth, encryption, hash, group)
	local mode = "Aggressive"

	-- try aggressive mode (diffie hellman group 2)
	version = send_ike_request(host, port, mode, transforms, 2, 'nmap')
	if version ~= nil then
		return {['mode']=mode, ['psk']=true, ['version']=version}
	end

	-- try aggressive mode (diffie hellman group 2, no id)
	-- some checkpoint devices respond to this
	version = send_ike_request(host, port, mode, transforms, 2, '')
	if version ~= nil then
		return {['mode']=mode, ['psk']=true, ['version']=version}
	end

	-- try aggressive mode (diffie hellman group 1)
	group = {"768"}
	transforms = generate_transforms(auth, encryption, hash, group)

	version = send_ike_request(host, port, mode, transforms, 1, 'nmap')
	if version ~= nil then
		return {['mode']=mode, ['psk']=true, ['version']=version}
	end

	-- try aggressive mode (diffie hellman group 5)
	group = {"1536"}
	transforms = generate_transforms(auth, encryption, hash, group)

	version = send_ike_request(host, port, mode, transforms, 5, 'nmap')
	if version ~= nil then
		return {['mode']=mode, ['psk']=true, ['version']=version}
	end

	-- generate aggressive mode multi auth transforms
	auth  = {"rsa", "Hybrid", "XAUTH"}
	encryption	= {"des", "3des", "aes/128", "aes/192", "aes/256"}
	hash  = {"md5", "sha1"} -- reduce the amout of hashes
	group = {"768", "1024", "1536"}
	transforms = generate_transforms(auth, encryption, hash, group)
	
	version = send_ike_request(host, port, mode, transforms, 2, 'nmap')
	if version ~= nil then
		return {['mode']=mode, ['psk']=false, ['version']=version}
	end


	--------------------------------------------------------------------------------
	-- Main mode
	--
	mode = "Main"
	auth  = {"psk", "rsa", "Hybrid", "XAUTH"}
	encryption	= {"des", "3des", "aes/128", "aes/192", "aes/256"}
	hash  = {"md5", "sha1"} -- reduce the amout of hashes
	group = {"768", "1024", "1536"}	

	version = send_ike_request(host, port, mode, transforms, 2, 'nmap')
	if version ~= nil then
		return {['mode']=mode, ['psk']=false, ['version']=version}
	end


	return false
end


action = function( host, port )
	local ike_response = get_version(host, port)
	local results = {['name'] = "Information"}

	if ike_response then
		-- Aggressive mode support
		local aggressive_mode = 'Aggressive mode is '
		if ike_response['mode'] == 'Aggressive' then
			aggressive_mode = aggressive_mode .. 'enabled'
		else
			aggressive_mode = aggressive_mode .. 'not detected'
		end
		table.insert(results, {['name']='Aggressive mode', aggressive_mode})

		-- Pre-shared key
		local psk = "Authentication via PSK is "
		if ike_response['psk'] then
			psk = psk .. 'enabled'
		else
			psk = psk .. 'not detected'
		end
		table.insert(results, {['name']='Pre-shared key', psk})

		-- extract ID
		local ids = ike_response['version']['response']['ID']
		if ids ~= nil then
			local id = { ['name'] = "ID details" }			
			for _, id_payload in pairs(ids) do			
				local t = ike.parse_id_type(id_payload)
				table.insert(id, {['name']='Type', t['type']})
				table.insert(id, {['name']='Value', t['value']})
			end
			table.insert(results, id)
		end

		-- extract vendor IDs
		if ike_response['version']['info'] ~= nil then
			local vids = { ['name'] = "Vendor IDs" }			
			for _,vid in ipairs(ike_response['version']['info']['matches']) do			
				table.insert(vids, vid['match'])
			end
			table.insert(results, vids)
		end
	end

	return stdnse.format_output(true, results)
end



