summaryrefslogtreecommitdiffstats
path: root/nselib/pop3.lua
diff options
context:
space:
mode:
Diffstat (limited to 'nselib/pop3.lua')
-rw-r--r--nselib/pop3.lua235
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;