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/pop3.lua | |
parent | Initial commit. (diff) | |
download | nmap-upstream.tar.xz nmap-upstream.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/pop3.lua')
-rw-r--r-- | nselib/pop3.lua | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/nselib/pop3.lua b/nselib/pop3.lua new file mode 100644 index 0000000..10d9c40 --- /dev/null +++ b/nselib/pop3.lua @@ -0,0 +1,235 @@ +--- +-- POP3 functions. +-- +-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html + +local base64 = require "base64" +local comm = require "comm" +local match = require "match" +local stdnse = require "stdnse" +local string = require "string" +local stringaux = require "stringaux" +local table = require "table" +_ENV = stdnse.module("pop3", stdnse.seeall) + +local HAVE_SSL, openssl = pcall(require,'openssl') + + +err = { + none = 0, + userError = 1, + pwError = 2, + informationMissing = 3, + OpenSSLMissing = 4, +} + +--- +-- Check a POP3 response for <code>"+OK"</code>. +-- @param line First line returned from an POP3 request. +-- @return The string <code>"+OK"</code> if found or <code>nil</code> otherwise. +function stat(line) + return string.match(line, "+OK") +end + + + +--- +-- Try to log in using the <code>USER</code>/<code>PASS</code> commands. +-- @param socket Socket connected to POP3 server. +-- @param user User string. +-- @param pw Password string. +-- @return Status (true or false). +-- @return Error code if status is false. +function login_user(socket, user, pw) + socket:send("USER " .. user .. "\r\n") + local status, line = socket:receive_lines(1) + if not stat(line) then return false, err.userError end + socket:send("PASS " .. pw .. "\r\n") + + status, line = socket:receive_lines(1) + + if stat(line) then return true, err.none + else return false, err.pwError + end +end + + +--- +-- Try to login using the the <code>AUTH</code> command using SASL/Plain method. +-- @param socket Socket connected to POP3 server. +-- @param user User string. +-- @param pw Password string. +-- @return Status (true or false). +-- @return Error code if status is false. +function login_sasl_plain(socket, user, pw) + + local auth64 = base64.enc(user .. "\0" .. user .. "\0" .. pw) + socket:send("AUTH PLAIN " .. auth64 .. "\r\n") + + local status, line = socket:receive_lines(1) + + if stat(line) then + return true, err.none + else + return false, err.pwError + end +end + +--- +-- Try to login using the <code>AUTH</code> command using SASL/Login method. +-- @param user User string. +-- @param pw Password string. +-- @param pw String containing password to login. +-- @return Status (true or false). +-- @return Error code if status is false. +function login_sasl_login(socket, user, pw) + + local user64 = base64.enc(user) + + local pw64 = base64.enc(pw) + + socket:send("AUTH LOGIN\r\n") + + local status, line = socket:receive_lines(1) + if not base64.dec(string.sub(line, 3)) == "User Name:" then + return false, err.userError + end + + socket:send(user64) + + local status, line = socket:receive_lines(1) + + if not base64.dec(string.sub(line, 3)) == "Password:" then + return false, err.userError + end + + socket:send(pw64) + + local status, line = socket:receive_lines(1) + + if stat(line) then + return true, err.none + else + return false, err.pwError + end +end + +--- +-- Try to login using the <code>APOP</code> command. +-- @param socket Socket connected to POP3 server. +-- @param user User string. +-- @param pw Password string. +-- @param challenge String containing challenge from POP3 server greeting. +-- @return Status (true or false). +-- @return Error code if status is false. +function login_apop(socket, user, pw, challenge) + if type(challenge) ~= "string" then return false, err.informationMissing end + + local apStr = stdnse.tohex(openssl.md5(challenge .. pw)) + socket:send(("APOP %s %s\r\n"):format(user, apStr)) + + local status, line = socket:receive_lines(1) + + if (stat(line)) then + return true, err.none + else + return false, err.pwError + end +end + +--- +-- Asks a POP3 server for capabilities. +-- +-- See RFC 2449. +-- @param host Host to be queried. +-- @param port Port to connect to. +-- @return Table containing capabilities or nil on error. +-- @return nil or String error message. +function capabilities(host, port) + + local socket, line, bopt, first_line = comm.tryssl(host, port, "" , {request_timeout=10000, recv_before=true}) + if not socket then + return nil, "Could Not Connect" + end + if not stat(first_line) then + return nil, "No Response" + end + + local capas = {} + if string.find(first_line, "<[%p%w]+>") then + capas.APOP = {} + end + + local status = socket:send("CAPA\r\n") + if( not(status) ) then + return nil, "Failed to send" + end + + status, line = socket:receive_buf(match.pattern_limit("%.", 2048), false) + if( not(status) ) then + return nil, "Failed to receive" + end + socket:close() + + local lines = stringaux.strsplit("\r\n",line) + if not stat(table.remove(lines,1)) then + capas.capa = false + return capas + end + + for _, line in ipairs(lines) do + if ( line and #line>0 ) then + local capability = line:sub(line:find("[%w-]+")) + line = line:sub(#capability + 2) + if ( line ~= "" ) then + capas[capability] = stringaux.strsplit(" ", line) + else + capas[capability] = {} + end + end + end + + return capas +end + +--- +-- Try to login using the <code>AUTH</code> command using SASL/CRAM-MD5 method. +-- @param socket Socket connected to POP3 server. +-- @param user User string. +-- @param pw Password string. +-- @return Status (true or false). +-- @return Error code if status is false. +function login_sasl_crammd5(socket, user, pw) + + socket:send("AUTH CRAM-MD5\r\n") + + local status, line = socket:receive_lines(1) + + local challenge = base64.dec(string.sub(line, 3)) + + local digest = stdnse.tohex(openssl.hmac('md5', pw, challenge)) + local authStr = base64.enc(user .. " " .. digest) + socket:send(authStr .. "\r\n") + + local status, line = socket:receive_lines(1) + + if stat(line) then + return true, err.none + else + return false, err.pwError + end +end + +-- Overwrite functions requiring OpenSSL if we got no OpenSSL. +if not HAVE_SSL then + + local no_ssl = function() + return false, err.OpenSSLMissing + end + + login_apop = no_ssl + login_sasl_crammd5 = no_ssl +end + + +return _ENV; |