mirror of
https://github.com/nmap/nmap.git
synced 2025-12-16 12:49:02 +00:00
Refactor TLS record/message parsing a bit to make it clearer.
This commit is contained in:
191
nselib/tls.lua
191
nselib/tls.lua
@@ -1114,71 +1114,9 @@ SCSVS = {
|
|||||||
["TLS_FALLBACK_SCSV"] = 0x5600, -- draft-ietf-tls-downgrade-scsv-00
|
["TLS_FALLBACK_SCSV"] = 0x5600, -- draft-ietf-tls-downgrade-scsv-00
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
message_parse = {
|
||||||
-- Read a SSL/TLS record
|
alert = function (buffer, j)
|
||||||
-- @param buffer The read buffer
|
local b = {}
|
||||||
-- @param i The position in the buffer to start reading
|
|
||||||
-- @param fragment Message fragment left over from previous record (nil if none)
|
|
||||||
-- @return The current position in the buffer
|
|
||||||
-- @return The record that was read, as a table
|
|
||||||
function record_read(buffer, i, fragment)
|
|
||||||
local b, h, len
|
|
||||||
local add = 0
|
|
||||||
|
|
||||||
------------
|
|
||||||
-- Header --
|
|
||||||
------------
|
|
||||||
|
|
||||||
-- Ensure we have enough data for the header.
|
|
||||||
if #buffer - i < TLS_RECORD_HEADER_LENGTH then
|
|
||||||
return i, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse header.
|
|
||||||
h = {}
|
|
||||||
local typ, proto, j = unpack(">BI2", buffer, i)
|
|
||||||
local name = find_key(TLS_CONTENTTYPE_REGISTRY, typ)
|
|
||||||
if name == nil then
|
|
||||||
stdnse.debug1("Unknown TLS ContentType: %d", typ)
|
|
||||||
return j, nil
|
|
||||||
end
|
|
||||||
h["type"] = name
|
|
||||||
name = find_key(PROTOCOLS, proto)
|
|
||||||
if name == nil then
|
|
||||||
stdnse.debug1("Unknown TLS Protocol: 0x%04x", proto)
|
|
||||||
return j, nil
|
|
||||||
end
|
|
||||||
h["protocol"] = name
|
|
||||||
|
|
||||||
h["length"], j = unpack(">I2", buffer, j)
|
|
||||||
|
|
||||||
-- Ensure we have enough data for the body.
|
|
||||||
len = j + h["length"] - 1
|
|
||||||
if #buffer < len then
|
|
||||||
return i, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Adjust buffer and length to account for message fragment left over
|
|
||||||
-- from last record.
|
|
||||||
if fragment then
|
|
||||||
add = #fragment
|
|
||||||
len = len + add
|
|
||||||
buffer = buffer:sub(1, j - 1) .. fragment .. buffer:sub(j, -1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Convert to human-readable form.
|
|
||||||
|
|
||||||
----------
|
|
||||||
-- Body --
|
|
||||||
----------
|
|
||||||
|
|
||||||
h["body"] = {}
|
|
||||||
|
|
||||||
while j <= len do
|
|
||||||
-- RFC 2246, 6.2.1 "multiple client messages of the same ContentType may
|
|
||||||
-- be coalesced into a single TLSPlaintext record"
|
|
||||||
b = {}
|
|
||||||
if h["type"] == "alert" then
|
|
||||||
-- Parse body.
|
-- Parse body.
|
||||||
b.level, b.description, j = unpack("BB", buffer, j)
|
b.level, b.description, j = unpack("BB", buffer, j)
|
||||||
|
|
||||||
@@ -1186,26 +1124,29 @@ function record_read(buffer, i, fragment)
|
|||||||
b["level"] = find_key(TLS_ALERT_LEVELS, b["level"])
|
b["level"] = find_key(TLS_ALERT_LEVELS, b["level"])
|
||||||
b["description"] = find_key(TLS_ALERT_REGISTRY, b["description"])
|
b["description"] = find_key(TLS_ALERT_REGISTRY, b["description"])
|
||||||
|
|
||||||
table.insert(h["body"], b)
|
return b, j
|
||||||
elseif h["type"] == "handshake" then
|
end,
|
||||||
|
handshake = function (buffer, j, protocol)
|
||||||
-- Check for message fragmentation.
|
-- Check for message fragmentation.
|
||||||
if len - j < 3 then
|
-- Need 4 bytes for message header with length
|
||||||
h.fragment = buffer:sub(j, len)
|
local have = #buffer - j + 1
|
||||||
return len + 1 - add, h
|
if have < 4 then
|
||||||
|
return nil, j, 4
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parse body.
|
-- Parse body.
|
||||||
local msg_end
|
local b = {}
|
||||||
b.type, msg_end, j = unpack("B>I3", buffer, j)
|
local len
|
||||||
msg_end = msg_end + j
|
b.type, len, j = unpack("B>I3", buffer, j)
|
||||||
|
local msg_end = len + j
|
||||||
|
|
||||||
-- Convert to human-readable form.
|
-- Convert to human-readable form.
|
||||||
b["type"] = find_key(TLS_HANDSHAKETYPE_REGISTRY, b["type"])
|
b["type"] = find_key(TLS_HANDSHAKETYPE_REGISTRY, b["type"])
|
||||||
|
|
||||||
-- Check for message fragmentation.
|
-- Check for message fragmentation.
|
||||||
if msg_end > len + 1 then
|
-- Need 4 bytes for header plus length of message
|
||||||
h.fragment = buffer:sub(j - 4, len)
|
if have < len + 4 then
|
||||||
return len + 1 - add, h
|
return nil, j - 4, len + 4
|
||||||
end
|
end
|
||||||
|
|
||||||
if b["type"] == "server_hello" then
|
if b["type"] == "server_hello" then
|
||||||
@@ -1213,7 +1154,7 @@ function record_read(buffer, i, fragment)
|
|||||||
b.protocol, b.time, b.random, b.session_id, j = unpack(">I2 I4 c28 s1", buffer, j)
|
b.protocol, b.time, b.random, b.session_id, j = unpack(">I2 I4 c28 s1", buffer, j)
|
||||||
b.cipher, b.compressor, j = unpack(">I2 B", buffer, j)
|
b.cipher, b.compressor, j = unpack(">I2 B", buffer, j)
|
||||||
-- Optional extensions for TLS only
|
-- Optional extensions for TLS only
|
||||||
if j < msg_end and h["protocol"] ~= "SSLv3" then
|
if j < msg_end and protocol ~= "SSLv3" then
|
||||||
local num_exts
|
local num_exts
|
||||||
b["extensions"] = {}
|
b["extensions"] = {}
|
||||||
num_exts, j = unpack(">I2", buffer, j)
|
num_exts, j = unpack(">I2", buffer, j)
|
||||||
@@ -1250,21 +1191,101 @@ function record_read(buffer, i, fragment)
|
|||||||
b.data, j = unpack("c" .. msg_end - j, buffer, j)
|
b.data, j = unpack("c" .. msg_end - j, buffer, j)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(h["body"], b)
|
return b, j
|
||||||
elseif h["type"] == "heartbeat" then
|
end,
|
||||||
|
heartbeat = function (buffer, j)
|
||||||
|
local b = {}
|
||||||
b.type, b.payload, j = unpack(">B s2", buffer, j)
|
b.type, b.payload, j = unpack(">B s2", buffer, j)
|
||||||
b.padding = buffer:sub(j, len)
|
-- Heartbeat messages are one per record; consume the rest of the record as padding.
|
||||||
j = len + 1
|
b.padding = buffer:sub(j)
|
||||||
table.insert(h["body"], b)
|
return b, #buffer + 1
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Read a SSL/TLS record
|
||||||
|
-- @param buffer The read buffer
|
||||||
|
-- @param i The position in the buffer to start reading
|
||||||
|
-- @param fragment Message fragment left over from previous record (nil if none)
|
||||||
|
-- @return The current position in the buffer
|
||||||
|
-- @return The record that was read, as a table
|
||||||
|
function record_read(buffer, i, fragment)
|
||||||
|
|
||||||
|
------------
|
||||||
|
-- Header --
|
||||||
|
------------
|
||||||
|
|
||||||
|
-- Ensure we have enough data for the header.
|
||||||
|
if #buffer - i < TLS_RECORD_HEADER_LENGTH then
|
||||||
|
return i, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse header.
|
||||||
|
local h = {}
|
||||||
|
local typ, proto, rlength, j = unpack(">B I2 I2", buffer, i)
|
||||||
|
h.length = rlength
|
||||||
|
local name = find_key(TLS_CONTENTTYPE_REGISTRY, typ)
|
||||||
|
if name == nil then
|
||||||
|
stdnse.debug1("Unknown TLS ContentType: %d", typ)
|
||||||
|
return j, nil
|
||||||
|
end
|
||||||
|
h["type"] = name
|
||||||
|
name = find_key(PROTOCOLS, proto)
|
||||||
|
if name == nil then
|
||||||
|
stdnse.debug1("Unknown TLS Protocol: 0x%04x", proto)
|
||||||
|
return j, nil
|
||||||
|
end
|
||||||
|
h["protocol"] = name
|
||||||
|
|
||||||
|
-- Ensure we have enough data for the body.
|
||||||
|
if #buffer < j + rlength - 1 then
|
||||||
|
return i, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Adjust buffer and length to account for message fragment left over
|
||||||
|
-- from last record.
|
||||||
|
local mbuffer
|
||||||
|
if fragment then
|
||||||
|
mbuffer = fragment .. buffer:sub(j, j + rlength)
|
||||||
else
|
else
|
||||||
|
mbuffer = buffer:sub(j, j + rlength)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Convert to human-readable form.
|
||||||
|
|
||||||
|
----------
|
||||||
|
-- Body --
|
||||||
|
----------
|
||||||
|
|
||||||
|
h["body"] = {}
|
||||||
|
|
||||||
|
local mi = 1
|
||||||
|
while mi < #mbuffer do
|
||||||
|
-- RFC 2246, 6.2.1 "multiple client messages of the same ContentType may
|
||||||
|
-- be coalesced into a single TLSPlaintext record"
|
||||||
|
local parser = message_parse[h.type]
|
||||||
|
if not parser then
|
||||||
stdnse.debug1("Unknown message type: %s", h["type"])
|
stdnse.debug1("Unknown message type: %s", h["type"])
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local b, need
|
||||||
|
b, mi, need = parser(mbuffer, mi, h.protocol)
|
||||||
|
if b then
|
||||||
|
table.insert(h.body, b)
|
||||||
|
elseif need then
|
||||||
|
stdnse.debug1("Fragmented after %d messages in %d bytes. %d remaining, need %d",
|
||||||
|
#h.body, mi, #mbuffer - mi + 1, need)
|
||||||
|
-- Fragmented message
|
||||||
|
h.fragment = mbuffer:sub(mi)
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ignore unparsed bytes.
|
-- Skip to the end of the record. Ignore unparsed bytes.
|
||||||
j = len + 1
|
-- These should be handled as fragmentation above
|
||||||
|
j = j + rlength
|
||||||
|
|
||||||
return j - add, h
|
return j, h
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user