PageRenderTime 159ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/nselib/pop3.lua

https://gitlab.com/g10h4ck/nmap-gsoc2015
Lua | 233 lines | 130 code | 50 blank | 53 comment | 22 complexity | 93ab1b346f256afb8c73848043c387ad MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
  1. ---
  2. -- POP3 functions.
  3. --
  4. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  5. local base64 = require "base64"
  6. local comm = require "comm"
  7. local stdnse = require "stdnse"
  8. local string = require "string"
  9. local table = require "table"
  10. _ENV = stdnse.module("pop3", stdnse.seeall)
  11. local HAVE_SSL, openssl = pcall(require,'openssl')
  12. err = {
  13. none = 0,
  14. userError = 1,
  15. pwError = 2,
  16. informationMissing = 3,
  17. OpenSSLMissing = 4,
  18. }
  19. ---
  20. -- Check a POP3 response for <code>"+OK"</code>.
  21. -- @param line First line returned from an POP3 request.
  22. -- @return The string <code>"+OK"</code> if found or <code>nil</code> otherwise.
  23. function stat(line)
  24. return string.match(line, "+OK")
  25. end
  26. ---
  27. -- Try to log in using the <code>USER</code>/<code>PASS</code> commands.
  28. -- @param socket Socket connected to POP3 server.
  29. -- @param user User string.
  30. -- @param pw Password string.
  31. -- @return Status (true or false).
  32. -- @return Error code if status is false.
  33. function login_user(socket, user, pw)
  34. socket:send("USER " .. user .. "\r\n")
  35. local status, line = socket:receive_lines(1)
  36. if not stat(line) then return false, err.user_error end
  37. socket:send("PASS " .. pw .. "\r\n")
  38. status, line = socket:receive_lines(1)
  39. if stat(line) then return true, err.none
  40. else return false, err.pwError
  41. end
  42. end
  43. ---
  44. -- Try to login using the the <code>AUTH</code> command using SASL/Plain method.
  45. -- @param socket Socket connected to POP3 server.
  46. -- @param user User string.
  47. -- @param pw Password string.
  48. -- @return Status (true or false).
  49. -- @return Error code if status is false.
  50. function login_sasl_plain(socket, user, pw)
  51. local auth64 = base64.enc(user .. "\0" .. user .. "\0" .. pw)
  52. socket:send("AUTH PLAIN " .. auth64 .. "\r\n")
  53. local status, line = socket:receive_lines(1)
  54. if stat(line) then
  55. return true, err.none
  56. else
  57. return false, err.pwError
  58. end
  59. end
  60. ---
  61. -- Try to login using the <code>AUTH</code> command using SASL/Login method.
  62. -- @param user User string.
  63. -- @param pw Password string.
  64. -- @param pw String containing password to login.
  65. -- @return Status (true or false).
  66. -- @return Error code if status is false.
  67. function login_sasl_login(socket, user, pw)
  68. local user64 = base64.enc(user)
  69. local pw64 = base64.enc(pw)
  70. socket:send("AUTH LOGIN\r\n")
  71. local status, line = socket:receive_lines(1)
  72. if not base64.dec(string.sub(line, 3)) == "User Name:" then
  73. return false, err.userError
  74. end
  75. socket:send(user64)
  76. local status, line = socket:receive_lines(1)
  77. if not base64.dec(string.sub(line, 3)) == "Password:" then
  78. return false, err.userError
  79. end
  80. socket:send(pw64)
  81. local status, line = socket:receive_lines(1)
  82. if stat(line) then
  83. return true, err.none
  84. else
  85. return false, err.pwError
  86. end
  87. end
  88. ---
  89. -- Try to login using the <code>APOP</code> command.
  90. -- @param socket Socket connected to POP3 server.
  91. -- @param user User string.
  92. -- @param pw Password string.
  93. -- @param challenge String containing challenge from POP3 server greeting.
  94. -- @return Status (true or false).
  95. -- @return Error code if status is false.
  96. function login_apop(socket, user, pw, challenge)
  97. if type(challenge) ~= "string" then return false, err.informationMissing end
  98. local apStr = stdnse.tohex(openssl.md5(challenge .. pw))
  99. socket:send(("APOP %s %s\r\n"):format(user, apStr))
  100. local status, line = socket:receive_lines(1)
  101. if (stat(line)) then
  102. return true, err.none
  103. else
  104. return false, err.pwError
  105. end
  106. end
  107. ---
  108. -- Asks a POP3 server for capabilities.
  109. --
  110. -- See RFC 2449.
  111. -- @param host Host to be queried.
  112. -- @param port Port to connect to.
  113. -- @return Table containing capabilities or nil on error.
  114. -- @return nil or String error message.
  115. function capabilities(host, port)
  116. local socket, line, bopt, first_line = comm.tryssl(host, port, "" , {request_timeout=10000, recv_before=true})
  117. if not socket then
  118. return nil, "Could Not Connect"
  119. end
  120. if not stat(first_line) then
  121. return nil, "No Response"
  122. end
  123. local capas = {}
  124. if string.find(first_line, "<[%p%w]+>") then
  125. capas.APOP = {}
  126. end
  127. local status = socket:send("CAPA\r\n")
  128. if( not(status) ) then
  129. return nil, "Failed to send"
  130. end
  131. status, line = socket:receive_buf("%.", false)
  132. if( not(status) ) then
  133. return nil, "Failed to receive"
  134. end
  135. socket:close()
  136. local lines = stdnse.strsplit("\r\n",line)
  137. if not stat(table.remove(lines,1)) then
  138. capas.capa = false
  139. return capas
  140. end
  141. for _, line in ipairs(lines) do
  142. if ( line and #line>0 ) then
  143. local capability = line:sub(line:find("[%w-]+"))
  144. line = line:sub(#capability + 2)
  145. if ( line ~= "" ) then
  146. capas[capability] = stdnse.strsplit(" ", line)
  147. else
  148. capas[capability] = {}
  149. end
  150. end
  151. end
  152. return capas
  153. end
  154. ---
  155. -- Try to login using the <code>AUTH</code> command using SASL/CRAM-MD5 method.
  156. -- @param socket Socket connected to POP3 server.
  157. -- @param user User string.
  158. -- @param pw Password string.
  159. -- @return Status (true or false).
  160. -- @return Error code if status is false.
  161. function login_sasl_crammd5(socket, user, pw)
  162. socket:send("AUTH CRAM-MD5\r\n")
  163. local status, line = socket:receive_lines(1)
  164. local challenge = base64.dec(string.sub(line, 3))
  165. local digest = stdnse.tohex(openssl.hmac('md5', pw, challenge))
  166. local authStr = base64.enc(user .. " " .. digest)
  167. socket:send(authStr .. "\r\n")
  168. local status, line = socket:receive_lines(1)
  169. if stat(line) then
  170. return true, err.none
  171. else
  172. return false, err.pwError
  173. end
  174. end
  175. -- Overwrite functions requiring OpenSSL if we got no OpenSSL.
  176. if not HAVE_SSL then
  177. local no_ssl = function()
  178. return false, err.OpenSSLMissing
  179. end
  180. login_apop = no_ssl
  181. login_sasl_crammd5 = no_ssl
  182. end
  183. return _ENV;