PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/nselib/membase.lua

https://github.com/prakashgamit/nmap
Lua | 334 lines | 191 code | 46 blank | 97 comment | 16 complexity | c48c7fd93644f0adea9d65c1e7bdd1c3 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.0, LGPL-2.1
  1. ---
  2. -- A smallish implementation of the Couchbase Membase TAP protocol
  3. -- Based on the scarce documentation from the Couchbase Wiki:
  4. -- x http://www.couchbase.org/wiki/display/membase/SASL+Authentication+Example
  5. --
  6. -- @author "Patrik Karlsson <patrik@cqure.net>"
  7. --
  8. local bin = require "bin"
  9. local match = require "match"
  10. local nmap = require "nmap"
  11. local sasl = require "sasl"
  12. local stdnse = require "stdnse"
  13. local table = require "table"
  14. _ENV = stdnse.module("membase", stdnse.seeall)
  15. -- A minimalistic implementation of the Couchbase Membase TAP protocol
  16. TAP = {
  17. -- Operations
  18. Op = {
  19. LIST_SASL_MECHS = 0x20,
  20. AUTHENTICATE = 0x21,
  21. },
  22. -- Requests
  23. Request = {
  24. -- Header breakdown
  25. -- Field (offset) (value)
  26. -- Magic (0): 0x80 (PROTOCOL_BINARY_REQ)
  27. -- Opcode (1): 0x00
  28. -- Key length (2-3): 0x0000 (0)
  29. -- Extra length (4): 0x00
  30. -- Data type (5): 0x00
  31. -- vbucket (6-7): 0x0000 (0)
  32. -- Total body (8-11): 0x00000000 (0)
  33. -- Opaque (12-15): 0x00000000 (0)
  34. -- CAS (16-23): 0x0000000000000000 (0)
  35. Header = {
  36. -- Creates a new instance of Header
  37. -- @param opcode number containing the operation
  38. -- @return o new instance of Header
  39. new = function(self, opcode)
  40. local o = {
  41. magic = 0x80,
  42. opcode = tonumber(opcode),
  43. keylen = 0x0000,
  44. extlen = 0x00,
  45. data_type = 0x00,
  46. vbucket = 0x0000,
  47. total_body = 0x00000000,
  48. opaque = 0x00000000,
  49. CAS = 0x0000000000000000,
  50. }
  51. setmetatable(o, self)
  52. self.__index = self
  53. return o
  54. end,
  55. -- Converts the header to string
  56. -- @return string containing the Header as string
  57. __tostring = function(self)
  58. return bin.pack(">CCSCCSIIL", self.magic, self.opcode, self.keylen,
  59. self.extlen, self.data_type, self.vbucket, self.total_body,
  60. self.opaque, self.CAS)
  61. end,
  62. },
  63. -- List SASL authentication mechanism
  64. SASLList = {
  65. -- Creates a new instance of the request
  66. -- @return o instance of request
  67. new = function(self)
  68. local o = {
  69. -- 0x20 SASL List Mechs
  70. header = TAP.Request.Header:new(TAP.Op.LIST_SASL_MECHS)
  71. }
  72. setmetatable(o, self)
  73. self.__index = self
  74. return o
  75. end,
  76. -- Converts the request to string
  77. -- @return string containing the request as string
  78. __tostring = function(self)
  79. return tostring(self.header)
  80. end,
  81. },
  82. -- Authenticates using SASL
  83. Authenticate = {
  84. -- Creates a new instance of the request
  85. -- @param username string containing the username
  86. -- @param password string containing the password
  87. -- @param mech string containing the SASL mechanism, currently suppored:
  88. -- PLAIN - plain-text authentication
  89. -- @return o instance of request
  90. new = function(self, username, password, mech)
  91. local o = {
  92. -- 0x20 SASL List Mechs
  93. header = TAP.Request.Header:new(TAP.Op.AUTHENTICATE),
  94. username = username,
  95. password = password,
  96. mech = mech,
  97. }
  98. setmetatable(o, self)
  99. self.__index = self
  100. return o
  101. end,
  102. -- Converts the request to string
  103. -- @return string containing the request as string
  104. __tostring = function(self)
  105. if ( self.mech == "PLAIN" ) then
  106. local mech_params = { self.username, self.password }
  107. local auth_data = sasl.Helper:new(self.mech):encode(table.unpack(mech_params))
  108. self.header.keylen = #self.mech
  109. self.header.total_body = #auth_data + #self.mech
  110. return tostring(self.header) .. self.mech .. auth_data
  111. end
  112. end,
  113. }
  114. },
  115. -- Responses
  116. Response = {
  117. -- The response header
  118. -- Header breakdown
  119. -- Field (offset) (value)
  120. -- Magic (0): 0x81 (PROTOCOL_BINARY_RES)
  121. -- Opcode (1): 0x00
  122. -- Key length (2-3): 0x0000 (0)
  123. -- Extra length (4): 0x00
  124. -- Data type (5): 0x00
  125. -- Status (6-7): 0x0000 (SUCCESS)
  126. -- Total body (8-11): 0x00000005 (5)
  127. -- Opaque (12-15): 0x00000000 (0)
  128. -- CAS (16-23): 0x0000000000000000 (0)
  129. Header = {
  130. -- Creates a new instance of Header
  131. -- @param data string containing the raw data
  132. -- @return o new instance of Header
  133. new = function(self, data)
  134. local o = {
  135. data = data
  136. }
  137. setmetatable(o, self)
  138. self.__index = self
  139. if ( o:parse() ) then
  140. return o
  141. end
  142. end,
  143. -- Parse the raw header and populates the class members
  144. -- @return status true on success, false on failure
  145. parse = function(self)
  146. if ( 24 > #self.data ) then
  147. stdnse.print_debug("membase: Header packet too short (%d bytes)", #self.data)
  148. return false, "Packet to short"
  149. end
  150. local pos
  151. pos, self.magic, self.opcode, self.keylen, self.extlen,
  152. self.data_type, self.status, self.total_body, self.opaque,
  153. self.CAS = bin.unpack(">CCSCCSIIL", self.data)
  154. return true
  155. end
  156. },
  157. -- Decoders
  158. Decoder = {
  159. -- TAP.Op.LIST_SASL_MECHS
  160. [0x20] = {
  161. -- Creates a new instance of the decoder
  162. -- @param data string containing the raw response
  163. -- @return o instance if successfully parsed, nil on failure
  164. -- the member variable <code>mechs</code> contains the
  165. -- supported authentication mechanisms.
  166. new = function(self, data)
  167. local o = { data = data }
  168. setmetatable(o, self)
  169. self.__index = self
  170. if ( o:parse() ) then
  171. return o
  172. end
  173. end,
  174. -- Parses the raw response
  175. -- @return true on success
  176. parse = function(self)
  177. self.mechs = self.data
  178. return true
  179. end
  180. },
  181. -- Login response
  182. [0x21] = {
  183. -- Creates a new instance of the decoder
  184. -- @param data string containing the raw response
  185. -- @return o instance if successfully parsed, nil on failure
  186. -- the member variable <code>status</code> contains the
  187. -- servers authentication response.
  188. new = function(self, data)
  189. local o = { data = data }
  190. setmetatable(o, self)
  191. self.__index = self
  192. if ( o:parse() ) then
  193. return o
  194. end
  195. end,
  196. -- Parses the raw response
  197. -- @return true on success
  198. parse = function(self)
  199. self.status = self.data
  200. return true
  201. end
  202. }
  203. }
  204. },
  205. }
  206. -- The Helper class is the main script interface
  207. Helper = {
  208. -- Creates a new instance of the helper
  209. -- @param host table as received by the action method
  210. -- @param port table as received by the action method
  211. -- @param options table including options to the helper, currently:
  212. -- <code>timeout</code> - socket timeout in milliseconds
  213. new = function(self, host, port, options)
  214. local o = {
  215. host = host,
  216. port = port,
  217. mech = stdnse.get_script_args("membase.authmech"),
  218. options = options or {}
  219. }
  220. setmetatable(o, self)
  221. self.__index = self
  222. return o
  223. end,
  224. -- Connects the socket to the server
  225. -- @return true on success, false on failure
  226. connect = function(self)
  227. self.socket = nmap.new_socket()
  228. self.socket:set_timeout(self.options.timeout or 10000)
  229. return self.socket:connect(self.host, self.port)
  230. end,
  231. -- Closes the socket
  232. close = function(self)
  233. return self.socket:close()
  234. end,
  235. -- Sends a request to the server, receives and parses the response
  236. -- @param req a Request instance
  237. -- @return status true on success, false on failure
  238. -- @return response instance of Response
  239. exch = function(self, req)
  240. local status, err = self.socket:send(tostring(req))
  241. if ( not(status) ) then
  242. return false, "Failed to send data"
  243. end
  244. local data
  245. status, data = self.socket:receive_buf(match.numbytes(24), true)
  246. if ( not(status) ) then
  247. return false, "Failed to receive data"
  248. end
  249. local header = TAP.Response.Header:new(data)
  250. if ( header.opcode ~= req.header.opcode ) then
  251. stdnse.print_debug("WARNING: Received invalid op code, request contained (%d), response contained (%d)", req.header.opcode, header.opcode)
  252. end
  253. if ( not(TAP.Response.Decoder[tonumber(header.opcode)]) ) then
  254. return false, ("No response handler for opcode: %d"):format(header.opcode)
  255. end
  256. local status, data = self.socket:receive_buf(match.numbytes(header.total_body), true)
  257. if ( not(status) ) then
  258. return false, "Failed to receive data"
  259. end
  260. local response = TAP.Response.Decoder[tonumber(header.opcode)]:new(data)
  261. if ( not(response) ) then
  262. return false, "Failed to parse response from server"
  263. end
  264. return true, response
  265. end,
  266. -- Gets list of supported SASL authentication mechanisms
  267. getSASLMechList = function(self)
  268. return self:exch(TAP.Request.SASLList:new())
  269. end,
  270. -- Logins to the server
  271. -- @param username string containing the username
  272. -- @param password string containing the password
  273. -- @param mech string containing the SASL mechanism to use
  274. -- @return status true on success, false on failure
  275. -- @return respons string containing "Auth failure" on failure
  276. login = function(self, username, password, mech)
  277. mech = mech or self.mech or "PLAIN"
  278. local status, response = self:exch(TAP.Request.Authenticate:new(username, password, mech))
  279. if ( not(status) ) then
  280. return false, "Auth failure"
  281. end
  282. if ( response.status == "Auth failure" ) then
  283. return false, response.status
  284. end
  285. return true, response.status
  286. end,
  287. }
  288. return _ENV;