diff --git a/nselib/iscsi.lua b/nselib/iscsi.lua
index 56d64b97d..f308be54f 100644
--- a/nselib/iscsi.lua
+++ b/nselib/iscsi.lua
@@ -25,6 +25,12 @@
-- ** The purpose of the class is to provide easy access to common iSCSI task
--
--
+-- @args iscsi.initiator-name The iSCSI qualified name (IQN) announced as the
+-- InitiatorName login key. Targets that only accept
+-- initiators matching a locally configured name reject the built-in
+-- default, which makes discovery and login fail with no output.
+-- Defaults to iqn.1991-05.com.microsoft:nmap_iscsi_probe.
+--
-- @author Patrik Karlsson
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
@@ -44,6 +50,27 @@ local stringaux = require "stringaux"
local table = require "table"
_ENV = stdnse.module("iscsi", stdnse.seeall)
+-- Script argument used to override the announced initiator name.
+local INITIATOR_NAME_ARG = "iscsi.initiator-name"
+
+-- IQN announced when the operator does not supply one.
+local DEFAULT_INITIATOR_NAME = "iqn.1991-05.com.microsoft:nmap_iscsi_probe"
+
+--- Returns the iSCSI qualified name (IQN) to announce as the
+-- InitiatorName login key.
+--
+-- @return string the IQN given through the iscsi.initiator-name
+-- script argument, or the built-in default if there is none.
+local function get_initiator_name()
+ local name = stdnse.get_script_args(INITIATOR_NAME_ARG)
+ -- An argument passed as a bare flag rather than as key=value comes back as a
+ -- number, which is not a usable IQN.
+ if type(name) ~= "string" then
+ return DEFAULT_INITIATOR_NAME
+ end
+ return name
+end
+
Packet = {
@@ -579,7 +606,7 @@ Helper = {
p:setTransit(true)
p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation)
- p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" )
+ p.kvp:add( "InitiatorName", get_initiator_name() )
p.kvp:add( "SessionType", "Discovery" )
p.kvp:add( "AuthMethod", "None" )
@@ -657,7 +684,7 @@ Helper = {
p:setTransit(true)
p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation)
- p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" )
+ p.kvp:add( "InitiatorName", get_initiator_name() )
p.kvp:add( "SessionType", "Normal" )
p.kvp:add( "TargetName", target_name )
p.kvp:add( "AuthMethod", auth_method )
@@ -723,7 +750,59 @@ Helper = {
}
+local unittest = require "unittest"
+if not unittest.testing() then
+ return _ENV
+end
+test_suite = unittest.TestSuite:new()
+do
+ local custom_iqn = "iqn.2005-03.org.open-iscsi:nmap"
+ local target_iqn = "iqn.2005-03.org.open-iscsi:target"
+
+ -- Runs a Helper method against a Comm stub that captures the request instead
+ -- of sending it, and returns the InitiatorName key it carried.
+ local function sent_initiator_name(method, ...)
+ local captured
+ local helper = Helper:new()
+ helper.comm = {
+ exchange = function(_, packet)
+ captured = packet
+ return false, "not sent"
+ end
+ }
+ helper[method](helper, ...)
+ return captured.kvp:get("InitiatorName")[1]
+ end
+
+ -- stdnse.get_script_args() also honours the bare suffix of an argument name,
+ -- so both spellings are isolated while fn runs and put back afterwards,
+ -- whether fn returns or raises.
+ local SHORT_ARG = string.match(INITIATOR_NAME_ARG, "[^.]+$")
+
+ local function with_arg(value, fn, ...)
+ local args = nmap.registry.args
+ local saved_long, saved_short = args[INITIATOR_NAME_ARG], args[SHORT_ARG]
+ args[INITIATOR_NAME_ARG], args[SHORT_ARG] = value, nil
+ local ok, result = pcall(fn, ...)
+ args[INITIATOR_NAME_ARG], args[SHORT_ARG] = saved_long, saved_short
+ assert(ok, result)
+ return result
+ end
+
+ test_suite:add_test(unittest.equal(
+ with_arg(nil, sent_initiator_name, "discoverTargets"), DEFAULT_INITIATOR_NAME),
+ "discovery announces the default IQN when no script-arg is given")
+ test_suite:add_test(unittest.equal(
+ with_arg(custom_iqn, sent_initiator_name, "discoverTargets"), custom_iqn),
+ "discovery announces the IQN given through " .. INITIATOR_NAME_ARG)
+ test_suite:add_test(unittest.equal(
+ with_arg(custom_iqn, sent_initiator_name, "login", target_iqn), custom_iqn),
+ "login announces the IQN given through " .. INITIATOR_NAME_ARG)
+ test_suite:add_test(unittest.equal(
+ with_arg(1, sent_initiator_name, "discoverTargets"), DEFAULT_INITIATOR_NAME),
+ INITIATOR_NAME_ARG .. " passed as a bare flag falls back to the default IQN")
+end
return _ENV;