1---
2-- Implements functionality related to Server Message Block (SMB, an extension
3-- of CIFS) traffic, which is a Windows protocol.
4--
5-- SMB traffic is normally sent to/from ports 139 or 445 of Windows systems. Other systems
6-- implement SMB as well, including Samba and a lot of embedded devices. Some of them implement
7-- it properly and many of them not. Although the protocol has been documented decently
8-- well by Samba and others, many 3rd party implementations are broken or make assumptions.
9-- Even Samba's and Windows' implementations aren't completely compatible. As a result,
10-- creating an implementation that accepts everything is a bit of a minefield. Microsoft's
11-- extensive documentation is available at the following URLs:
12-- * SMB: http://msdn.microsoft.com/en-us/library/cc246231(v=prot.13).aspx
13-- * CIFS: http://msdn.microsoft.com/en-us/library/ee442092(v=prot.13).aspx
14--
15-- Where possible, this implementation, since it's intended for scanning, will attempt to
16-- accept any invalid implementations it can, and fail gracefully if it can't. This has
17-- been tested against a great number of weird implementations, and it now works against
18-- all of them.
19--
20-- The intention of this library is to eventually handle all aspects of the SMB protocol.
21-- That being said, I'm only implementing the pieces that I (Ron Bowes) need. If you
22-- require something more, let me know and I'll put it on my todo list.
23--
24-- A programmer using this library should already have some knowledge of the SMB protocol,
25-- although a lot isn't necessary. You can pick up a lot by looking at the code. The basic
26-- login/logoff is this:
27--
28-- <code>
29-- [connect]
30-- C->S SMB_COM_NEGOTIATE
31-- S->C SMB_COM_NEGOTIATE
32-- C->S SMB_COM_SESSION_SETUP_ANDX
33-- S->C SMB_COM_SESSION_SETUP_ANDX
34-- C->S SMB_COM_TREE_CONNECT_ANDX
35-- S->C SMB_COM_TREE_CONNECT_ANDX
36-- ...
37-- C->S SMB_COM_TREE_DISCONNECT
38-- S->C SMB_COM_TREE_DISCONNECT
39-- C->S SMB_COM_LOGOFF_ANDX
40-- S->C SMB_COM_LOGOFF_ANDX
41-- [disconnect]
42-- </code>
43--
44-- In terms of functions here, the protocol is:
45--
46-- <code>
47-- status, smbstate = smb.start(host)
48-- status, err      = smb.negotiate_protocol(smbstate, {})
49-- status, err      = smb.start_session(smbstate, {})
50-- status, err      = smb.tree_connect(smbstate, path, {})
51-- ...
52-- status, err      = smb.tree_disconnect(smbstate)
53-- status, err      = smb.logoff(smbstate)
54-- status, err      = smb.stop(smbstate)
55-- </code>
56--
57-- The <code>stop</code> function will automatically call tree_disconnect and logoff,
58-- cleaning up the session, if it hasn't been done already.
59--
60-- To initially begin the connection, there are two options:
61--
62-- 1) Attempt to start a raw session over 445, if it's open.
63--
64-- 2) Attempt to start a NetBIOS session over 139. Although the
65--    protocol's the same, it requires a <code>session request</code> packet.
66--    That packet requires the computer's name, which is requested
67--    using a NBSTAT probe over UDP port 137.
68--
69-- Once it's connected, a <code>SMB_COM_NEGOTIATE</code> packet is sent, requesting the protocol
70-- "NT LM 0.12", which is the most commonly supported one. Among other things, the server's
71-- response contains the host's security level, the system time, and the computer/domain name.
72-- Some systems will refuse to use that protocol and return "-1" or "1" instead of 0. If that's
73-- detected, we kill the connection (because the protocol following won't work).
74--
75-- If that's successful, <code>SMB_COM_SESSION_SETUP_ANDX</code> is sent. It is essentially the logon
76-- packet, where the username, domain, and password are sent to the server for verification.
77-- The username and password are generally picked up from the program parameters, which are
78-- set when running a script, or from the registry where it can be set by other scripts (for
79-- example, <code>smb-brute.nse</code>). However, they can also be passed as parameters to the
80-- function, which will override any other username/password set.
81--
82-- If a username and password are set, they are used for the first login attempt. If a login fails,
83-- or they weren't set, a connection as the 'GUEST' account with a blank password is attempted. If
84-- that fails, then a NULL session is established, which should always work. The username/password
85-- will give the highest access level, GUEST will give lower access, and NULL will give the lowest
86-- (often, NULL will give no access).
87--
88-- The actual login protocol used by <code>SMB_COM_SESSION_SETUP_ANDX</code> is explained in detail
89-- in <code>smbauth.lua</code>.
90--
91-- Thanks go to Christopher R. Hertel and his book Implementing CIFS, which
92-- taught me everything I know about Microsoft's protocols. Additionally, I used Samba's
93-- list of error codes for my constants. Although I don't believe they would be covered
94-- by GPL, since they're public now anyways, but I'm not a lawyer and, if somebody feels
95-- differently, let me know and we can sort this out.
96--
97-- Scripts that use this module can use the script arguments listed below
98-- example of using these script arguments:
99-- <code>
100-- nmap --script=smb-<script>.nse --script-args=smbuser=ron,smbpass=iagotest2k3,smbbasic=1,smbsign=force <host>
101-- </code>
102--
103-- @args  smbbasic    Forces the authentication to use basic security, as opposed to "extended security".
104--                   Against most modern systems, extended security should work, but there may be cases
105--                   where you want to force basic. There's a chance that you'll get better results for
106--                   enumerating users if you turn on basic authentication.
107-- @args smbsign      Controls whether or not server signatures are checked in SMB packets. By default, on Windows,
108--                   server signatures aren't enabled or required. By default, this library will always sign
109--                   packets if it knows how, and will check signatures if the server says to. Possible values are:
110-- * <code>force</code>:      Always check server signatures, even if server says it doesn't support them (will
111--                           probably fail, but is technically more secure).
112-- * <code>negotiate</code>: [default] Use signatures if server supports them.
113-- * <code>ignore</code>:    Never check server signatures. Not recommended.
114-- * <code>disable</code>:   Don't send signatures, at all, and don't check the server's. not recommended.
115--                   More information on signatures can be found in <code>smbauth.lua</code>.
116-- @args smbport      Override the default port choice. If <code>smbport</code> is open, it's used. It's assumed
117--                   to be the same protocol as port 445, not port 139. Since it probably isn't possible to change
118--                   Windows' ports normally, this is mostly useful if you're bouncing through a relay or something.
119-- @args randomseed   Set to a value to change the filenames/service names that are randomly generated.
120--
121-- @author Ron Bowes <ron@skullsecurity.net>
122-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
123-----------------------------------------------------------------------
124local asn1 = require "asn1"
125local datetime = require "datetime"
126local io = require "io"
127local math = require "math"
128local match = require "match"
129local netbios = require "netbios"
130local nmap = require "nmap"
131local smbauth = require "smbauth"
132local stdnse = require "stdnse"
133local string = require "string"
134local table = require "table"
135local tableaux = require "tableaux"
136local unicode = require "unicode"
137local smb2 = require "smb2"
138_ENV = stdnse.module("smb", stdnse.seeall)
139
140-- These arrays are filled in with constants at the bottom of this file
141command_codes = {}
142command_names = {}
143status_codes = {}
144status_names = {}
145filetype_codes = {}
146filetype_names = {}
147
148local TIMEOUT = 10000
149
150---Wrapper around <code>smbauth.add_account</code>.
151function add_account(host, username, domain, password, password_hash, hash_type, is_admin)
152  smbauth.add_account(host, username, domain, password, password_hash, hash_type, is_admin)
153end
154
155---Wrapper around <code>smbauth.get_account</code>.
156function get_account(host)
157  return smbauth.get_account(host)
158end
159---Create an 'overrides' table
160function get_overrides(username, domain, password, password_hash, hash_type, overrides)
161  if(not(overrides)) then
162    return {username=username, domain=domain, password=password, password_hash=password_hash, hash_type=hash_type}
163  else
164    overrides['username'] = username
165    overrides['domain'] = domain
166    overrides['password'] = password
167    overrides['password_hash'] = password_hash
168    overrides['hash_type'] = hash_type
169  end
170end
171
172---Get an 'overrides' table for the anonymous user
173--
174--@param overrides [optional] A base table of overrides. The appropriate fields will be added.
175function get_overrides_anonymous(overrides)
176  if(not(overrides)) then
177    return {username='', domain='', password='', password_hash=nil, hash_type='none'}
178  else
179    overrides['username'] = ''
180    overrides['domain'] = ''
181    overrides['password'] = ''
182    overrides['password_hash'] = ''
183    overrides['hash_type'] = 'none'
184  end
185end
186
187---Convert a status number from the SMB header into a status name, returning an error message (not nil) if
188-- it wasn't found.
189--
190--@param status The numerical status.
191--@return A string representing the error. Never nil.
192function get_status_name(status)
193
194  if(status_names[status] == nil) then
195    -- If the name wasn't found in the array, do a linear search on it
196    for i, v in pairs(status_names) do
197      if(v == status) then
198        return i
199      end
200    end
201
202    return string.format("NT_STATUS_UNKNOWN (0x%08x)", status)
203  else
204    return status_names[status]
205  end
206end
207
208
209--- Determines whether or not SMB checks are possible on this host, and, if they are,
210--  which port is best to use. This is how it decides:
211--
212-- * If port tcp/445 is open, use it for a raw connection
213-- * Otherwise, if ports tcp/139 and udp/137 are open, do a NetBIOS connection. Since UDP scanning isn't default, we're also ok with udp/137 in an unknown state.
214--
215--@param host The host object.
216--@return The port number to use, or nil if we don't have an SMB port
217function get_port(host)
218  local port_u137 = nmap.get_port_state(host, {number=137, protocol="udp"})
219  local port_t139 = nmap.get_port_state(host, {number=139, protocol="tcp"})
220  local port_t445 = nmap.get_port_state(host, {number=445, protocol="tcp"})
221  local custom_port = nil
222
223  if(nmap.registry.args.smbport ~= nil) then
224    custom_port = nmap.get_port_state(host, {number=tonumber(nmap.registry.args.smbport), protocol="tcp"})
225  end
226
227  -- Try a user-defined port first
228  if(custom_port ~= nil and custom_port.state == "open") then
229    return custom_port.number
230  end
231
232  if(port_t445 ~= nil and port_t445.state == "open") then
233    -- tcp/445 is open, we're good
234    return 445
235  end
236
237  if(port_t139 ~= nil and port_t139.state == "open") then
238    -- tcp/139 is open, check uf udp/137 is open or unknown
239    if(port_u137 == nil or port_u137.state == "open" or port_u137.state == "open|filtered") then
240      return 139
241    end
242  end
243
244  return nil
245end
246
247---Turn off extended security negotiations for this connection.
248--
249-- There are a few reasons you might want to do that, the main ones being that
250-- extended security is going to be marginally slower and it's not going to
251-- give the same level of information in some cases (namely, it doesn't present
252-- the server's name).
253--@param smb The SMB state table.
254function disable_extended(smb)
255  smb['extended_security'] = false
256end
257
258--- Begins a SMB session, automatically determining the best way to connect.
259--
260-- @param host The host object
261-- @return (status, smb) if the status is true, result is the newly crated smb object;
262--         otherwise, socket is the error message.
263function start(host)
264  local port = get_port(host)
265  local status, result
266  local state = {}
267
268  state['uid']      = 0
269  state['tid']      = 0
270  state['mid']      = 1
271  state['pid']      = math.random(32766) + 1
272  state['host']     = host
273  state['ip']       = host.ip
274  state['sequence'] = -1
275
276  -- Check whether or not the user requested basic authentication
277  if(stdnse.get_script_args( "smbbasic" )) then
278    state['extended_security'] = false
279  else
280    state['extended_security'] = true
281  end
282
283  -- Store the name of the server
284  local nbcache_mutex = nmap.mutex("Netbios lookup mutex")
285  nbcache_mutex "lock"
286  if ( not(host.registry['netbios_name']) ) then
287    status, result = netbios.get_server_name(host.ip)
288    if(status == true) then
289      host.registry['netbios_name'] = result
290      state['name'] = result
291    end
292  else
293    stdnse.debug2("SMB: Resolved netbios name from cache")
294    state['name'] = host.registry['netbios_name']
295  end
296  nbcache_mutex "done"
297
298  stdnse.debug2("SMB: Starting SMB session for %s (%s)", host.name, host.ip)
299
300  if(port == nil) then
301    return false, "SMB: Couldn't find a valid port to check"
302  end
303
304  -- Initialize the accounts for logging on
305  smbauth.init_account(host)
306
307  if(port ~= 139) then
308    status, state['socket'] = start_raw(host, port)
309    state['port'] = port
310
311    if(status == false) then
312      return false, state['socket']
313    end
314    return true, state
315
316  else
317    status, state['socket'] = start_netbios(host, port)
318    state['port'] = port
319    if(status == false) then
320      return false, state['socket']
321    end
322    return true, state
323
324  end
325
326  return false, "SMB: Couldn't find a valid port to check"
327end
328
329---Initiates a SMB connection over whichever port it can, then optionally sends
330-- the common initialization packets.
331--
332-- Note that each packet depends on the previous one, so if you want to go all
333-- the way up to create_file, you have to set all parameters.
334--
335-- If anything fails, we back out of the connection and return an error, so the
336-- calling function doesn't have to call smb.stop().
337--
338--@param host The host object.
339--@param bool_negotiate_protocol [optional] If 'true', send the protocol
340--                               negotiation. Default: false.
341--@param bool_start_session [optional] If 'true', start the session. Default:
342--                          false.
343--@param str_tree_connect [optional] The tree to connect to, if given (eg.
344--                        "IPC$" or "C$"). If not given, packet isn't sent.
345--@param str_create_file [optional] The path and name of the file (or pipe)
346--                       that's created, if given. If not given, packet isn't
347--                       sent.
348--@param overrides [optional] A table of overrides (for, for example, username,
349--                 password, etc.) to pass to all functions.
350--@param bool_disable_extended [optional] If set to true, disables extended
351--                             security negotiations.
352function start_ex(host, bool_negotiate_protocol, bool_start_session, str_tree_connect, str_create_file, bool_disable_extended, overrides)
353  local smbstate
354  local status, err
355
356  -- Make sure we have overrides
357  overrides = overrides or {}
358
359  -- Begin the SMB session
360  status, smbstate = start(host)
361  if(status == false) then
362    return false, smbstate
363  end
364
365  -- Disable extended security if it was requested
366  if(bool_disable_extended == true) then
367    disable_extended(smbstate)
368  end
369
370  if(bool_negotiate_protocol == true) then
371    -- Negotiate the protocol
372    status, err = negotiate_protocol(smbstate, overrides)
373    if(status == false) then
374      stop(smbstate)
375      return false, err
376    end
377
378    if(bool_start_session == true) then
379      -- Start up a session
380      status, err = start_session(smbstate, overrides)
381      if(status == false) then
382        stop(smbstate)
383        return false, err
384      end
385
386      if(str_tree_connect ~= nil) then
387        -- Connect to share
388        status, err = tree_connect(smbstate, str_tree_connect, overrides)
389        if(status == false) then
390          stop(smbstate)
391          return false, err
392        end
393
394        if(str_create_file ~= nil) then
395          -- Try to connect to requested pipe
396          status, err = create_file(smbstate, str_create_file, overrides)
397          if(status == false) then
398            stop(smbstate)
399            return false, err
400          end
401        end
402      end
403    end
404  end
405
406  -- Return everything
407  return true, smbstate
408end
409
410--- Kills the SMB connection and closes the socket.
411--
412--  In addition to killing the connection, this function will log off the user and disconnect
413--  the connected tree, if possible.
414--
415--@param smb    The SMB object associated with the connection
416--@return (status, result) If status is false, result is an error message. Otherwise, result
417--        is undefined.
418function stop(smb)
419
420  if(smb['tid'] ~= 0) then
421    tree_disconnect(smb)
422  end
423
424  if(smb['uid'] ~= 0) then
425    logoff(smb)
426  end
427
428  stdnse.debug2("SMB: Closing socket")
429  if(smb['socket'] ~= nil) then
430    local status, err = smb['socket']:close()
431
432    if(status == false) then
433      return false, "SMB: Failed to close socket: " .. err
434    end
435  end
436
437  return true
438end
439
440--- Begins a raw SMB session, likely over port 445. Since nothing extra is required, this
441--  function simply makes a connection and returns the socket.
442--
443--@param host The host object to check.
444--@param port The port to use (most likely 445).
445--@return (status, socket) if status is true, result is the newly created socket.
446--        Otherwise, socket is the error message.
447function start_raw(host, port)
448  local status, err
449  local socket = nmap.new_socket()
450
451  socket:set_timeout(TIMEOUT)
452  status, err = socket:connect(host, port, "tcp")
453
454  if(status == false) then
455    return false, "SMB: Failed to connect to host: " .. err
456  end
457
458  return true, socket
459end
460
461--- This function will take a string like "a.b.c.d" and return "a", "a.b", "a.b.c", and "a.b.c.d".
462--
463--  This is used for discovering NetBIOS names. If a NetBIOS name is unknown, the substrings of the
464--  DNS name can be used in this way.
465--
466--@param name The name to take apart
467--@return An array of the sub names
468local function get_subnames(name)
469  local i = -1
470  local list = {}
471
472  repeat
473    local subname = name
474
475    i = string.find(name, "[.]", i + 1)
476    if(i ~= nil) then
477      subname = string.sub(name, 1, i - 1)
478    end
479
480    list[#list + 1] = string.upper(subname)
481
482  until i == nil
483
484  return list
485end
486
487--- Begins a SMB session over NetBIOS.
488--
489-- This requires a NetBIOS Session Start message to be sent first, which in
490-- turn requires the NetBIOS name. The name can be provided as a parameter, or
491-- it can be automatically determined.
492--
493-- Automatically determining the name is interesting, to say the least. Here
494-- are the names it tries, and the order it tries them in:
495-- * The name the user provided, if present
496-- * The name pulled from NetBIOS (udp/137), if possible
497-- * The generic name "*SMBSERVER"
498-- * Each subset of the domain name (for example, scanme.insecure.org would
499--   attempt "scanme", "scanme.insecure", and "scanme.insecure.org")
500--
501-- This whole sequence is a little hackish, but it's the standard way of doing
502-- it.
503--
504--@param host The host object to check.
505--@param port The port to use (most likely 139).
506--@param name [optional] The NetBIOS name of the host. Will attempt to
507--            automatically determine if it isn't given.
508--@return (status, socket) if status is true, result is the port
509--        Otherwise, socket is the error message.
510function start_netbios(host, port, name)
511  local i
512  local status, err
513  local pos, result, flags, length
514  local socket = nmap.new_socket()
515
516  -- First, populate the name array with all possible names, in order of significance
517  local names = {}
518
519  -- Use the name parameter
520  if(name ~= nil) then
521    names[#names + 1] = name
522  end
523
524  -- Get the name of the server from NetBIOS
525  status, name = netbios.get_server_name(host.ip)
526  if(status == true) then
527    names[#names + 1] = name
528  end
529
530  -- "*SMBSERVER" is a special name that any server should respond to
531  names[#names + 1] = "*SMBSERVER"
532
533  -- If all else fails, use each substring of the DNS name (this is a HUGE hack, but is actually
534  -- a recommended way of doing this!)
535  if(host.name ~= nil and host.name ~= "") then
536    local new_names = get_subnames(host.name)
537    for i = 1, #new_names, 1 do
538      names[#names + 1] = new_names[i]
539    end
540  end
541
542  -- This loop will try all the NetBIOS names we've collected, hoping one of them will work. Yes,
543  -- this is a hackish way, but it's actually the recommended way.
544  i = 1
545  repeat
546
547    -- Use the current name
548    name = names[i]
549
550    -- Some debug information
551    stdnse.debug1("SMB: Trying to start NetBIOS session with name = '%s'", name)
552    -- Request a NetBIOS session
553    local session_request = string.pack(">BBI2zz",
554      0x81,                        -- session request
555      0x00,                        -- flags
556      0x44,                        -- length
557      netbios.name_encode(name),   -- server name
558      netbios.name_encode("NMAP")  -- client name
559      );
560
561    stdnse.debug3("SMB: Connecting to %s", host.ip)
562    socket:set_timeout(TIMEOUT)
563    status, err = socket:connect(host, port, "tcp")
564    if(status == false) then
565      socket:close()
566      return false, "SMB: Failed to connect: " .. err
567    end
568
569    -- Send the session request
570    stdnse.debug3("SMB: Sending NetBIOS session request with name %s", name)
571    status, err = socket:send(session_request)
572    if(status == false) then
573      socket:close()
574      return false, "SMB: Failed to send: " .. err
575    end
576    socket:set_timeout(TIMEOUT)
577
578    -- Receive the session response
579    stdnse.debug3("SMB: Receiving NetBIOS session response")
580    status, result = socket:receive_buf(match.numbytes(4), true);
581    if(status == false) then
582      socket:close()
583      return false, "SMB: Failed to close socket: " .. result
584    end
585    result, flags, length, pos = string.unpack(">BBI2", result)
586
587    -- Check for a positive session response (0x82)
588    if result == 0x82 then
589      stdnse.debug3("SMB: Successfully established NetBIOS session with server name %s", name)
590      return true, socket
591    end
592
593    -- If the session failed, close the socket and try the next name
594    stdnse.debug1("SMB: Session request failed, trying next name")
595    socket:close()
596
597    -- Try the next name
598    i = i + 1
599
600  until i > #names
601
602  -- We reached the end of our names list
603  stdnse.debug1("SMB: None of the NetBIOS names worked!")
604  return false, "SMB: Couldn't find a NetBIOS name that works for the server. Sorry!"
605end
606
607--- Creates a string containing a SMB packet header. The header looks like this:
608--
609--<code>
610-- --------------------------------------------------------------------------------------------------
611-- | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9  8  7  6  5  4  3  2  1  0 |
612-- --------------------------------------------------------------------------------------------------
613-- |         0xFF           |          'S'          |        'M'            |         'B'           |
614-- --------------------------------------------------------------------------------------------------
615-- |        Command         |                             Status...                                 |
616-- --------------------------------------------------------------------------------------------------
617-- |    ...Status           |        Flags          |                    Flags2                     |
618-- --------------------------------------------------------------------------------------------------
619-- |                    PID_high                    |                  Signature.....               |
620-- --------------------------------------------------------------------------------------------------
621-- |                                        ....Signature....                                       |
622-- --------------------------------------------------------------------------------------------------
623-- |              ....Signature                     |                    Unused                     |
624-- --------------------------------------------------------------------------------------------------
625-- |                      TID                       |                     PID                       |
626-- --------------------------------------------------------------------------------------------------
627-- |                      UID                       |                     MID                       |
628-- -------------------------------------------------------------------------------------------------
629--</code>
630--
631-- All fields are, incidentally, encoded in little endian byte order.
632--
633-- For the purposes here, the program doesn't care about most of the fields so they're given default
634-- values. The "command" field is the only one we ever have to set manually, in my experience. The TID
635-- and UID need to be set, but those are stored in the smb state and don't require user intervention.
636--
637--@param smb     The smb state table.
638--@param command The command to use.
639--@param overrides The overrides table. Keep in mind that overriding things like flags is generally a very bad idea, unless you know what you're doing.
640--@return A binary string containing the packed packet header.
641function smb_encode_header(smb, command, overrides)
642  -- Make sure we have an overrides array
643  overrides = overrides or {}
644
645  -- Used for the header
646  local sig = "\xFFSMB"
647
648  -- Pretty much every flags is deprecated. We set these two because they're required to be on.
649  local flags  = (0x10 | 0x08) -- SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES
650  -- These flags are less deprecated. We negotiate 32-bit status codes and long names. We also don't include Unicode, which tells
651  -- the server that we deal in ASCII.
652  local flags2 = (0x4000 | 0x2000 | 0x0040 | 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_EXECUTE_ONLY_READS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAMES
653
654  -- Unless the user's disabled the security signature, add it
655  if(nmap.registry.args.smbsign ~= "disable") then
656    flags2 = (flags2 | 0x0004) -- SMB_FLAGS2_SECURITY_SIGNATURE
657  end
658
659
660  if(smb['extended_security'] == true) then
661    flags2 = (flags2 | 0x0800) -- SMB_EXTENDED_SECURITY
662  end
663
664  -- TreeID should never ever be 'nil', but it seems to happen once in awhile so print an error
665  if(smb['tid'] == nil) then
666    return false, string.format("SMB: ERROR: TreeID value was set to nil on host %s", smb['ip'])
667  end
668
669  local header = string.pack("<BBBBB I4 B I2 I2 I8 I2 I2 I2 I2 I2",
670    sig:byte(1),  -- Header
671    sig:byte(2),  -- Header
672    sig:byte(3),  -- Header
673    sig:byte(4),  -- Header
674    command,      -- Command
675    (overrides['status'] or 0),        -- status
676    (overrides['flags'] or flags),     -- flags
677    (overrides['flags2'] or flags2),   -- flags2
678    (overrides['pid_high'] or 0),      -- extra (pid_high)
679    (overrides['signature'] or 0),     -- extra (signature)
680    (overrides['extra'] or 0),         -- extra (unused)
681    (overrides['tid'] or smb['tid']),  -- tid
682    (overrides['pid'] or smb['pid']),  -- pid
683    (overrides['uid'] or smb['uid']),  -- uid
684    (overrides['mid'] or smb['mid'])   -- mid
685    )
686
687  return header
688end
689
690--- Converts a string containing the parameters section into the encoded
691--  parameters string.
692--
693-- The encoding is simple:
694-- * (1 byte)   The number of 2-byte values in the parameters section
695-- * (variable) The parameter section
696-- This is automatically done by <code>smb_send</code>.
697--
698-- @param parameters The parameters section.
699-- @param overrides The overrides table. The only thing possible to override here is the length.
700-- @return The encoded parameters.
701local function smb_encode_parameters(parameters, overrides)
702  -- Make sure we have an overrides array
703  overrides = overrides or {}
704
705  return string.pack("<B", overrides['parameters_length'] or (#parameters / 2)) .. parameters
706end
707
708--- Converts a string containing the data section into the encoded data string.
709--
710-- The encoding is simple:
711-- * (2 bytes)  The number of bytes in the data section
712-- * (variable) The data section
713-- This is automatically done by <code>smb_send</code>.
714--
715-- @param data The data section.
716-- @param overrides The overrides table. The only thing possible to override here is the length.
717-- @return The encoded data.
718local function smb_encode_data(data, overrides)
719  -- Make sure we have an overrides array
720  overrides = overrides or {}
721
722  return string.pack("<I2", overrides['data_length'] or #data) .. data
723end
724
725---Sign the message, if possible. This is done by replacing the signature with the sequence
726-- number, creating a hash, then putting that hash in the signature location.
727--@param smb  The smb state object.
728--@param body The body of the packet that's being signed.
729--@return The body of the packet, with the signature in place.
730local function message_sign(smb, body)
731  smb['sequence'] = smb['sequence'] + 1
732
733  if(smb['mac_key'] == nil) then
734    stdnse.debug3("SMB: Not signing message (missing mac_key)")
735    return body
736  elseif(nmap.registry.args.smbsign == "disable") then
737    stdnse.debug3("SMB: Not signing message (disabled by user)")
738
739    return body
740  end
741
742  -- Convert the sequence number to a string
743  local sequence = string.pack("<I8", smb['sequence'])
744  -- Create a new string, with the sequence number in place
745  local new_packet = string.sub(body, 1, 14) .. sequence .. string.sub(body, 23)
746  -- Calculate the signature
747  local signature = smbauth.calculate_signature(smb['mac_key'], new_packet)
748
749  return string.sub(body, 1, 14) .. signature .. string.sub(body, 23)
750end
751
752---Check the signature of the message.
753--
754-- This is the opposite of <code>message_sign</code>, and works the same way
755-- (replaces the signature with the sequence number, calculates hash, checks)
756--@param smb  The smb state object.
757--@param body The body of the packet that's being checked.
758--@return A true/false value -- true if the packet was signed properly, false if it wasn't.
759local function message_check_signature(smb, body)
760  smb['sequence'] = smb['sequence'] + 1
761
762  if(smb['mac_key'] == nil) then
763    stdnse.debug3("SMB: Not signing message (missing mac_key)")
764    return true
765  elseif(nmap.registry.args.smbsign ~= "force" and (smb['security_mode'] & 0x0A) ~= 0) then
766    stdnse.debug3("SMB: Not signing message (server doesn't support it -- default)")
767    return true
768  elseif(nmap.registry.args.smbsign == "disable" or nmap.registry.args.smbsign == "ignore") then
769    stdnse.debug3("SMB: Not signing message (disabled by user)")
770    return true
771  end
772
773  -- Pull out the signature that they used
774  local signature  = string.sub(body, 15, 22)
775
776  -- Turn the sequence into a string
777  local sequence   = string.pack("<I8", smb['sequence'])
778  -- Create a new string, with the sequence number in place
779  local new_packet = string.sub(body, 1, 14) .. sequence .. string.sub(body, 23)
780
781  -- Calculate the proper signature
782  local real_signature = smbauth.calculate_signature(smb['mac_key'], new_packet)
783
784  -- Validate the signature
785  return signature == real_signature
786end
787
788--- Prepends the NetBIOS header to the packet, which is essentially the length, encoded
789--  in 4 bytes of big endian, and sends it out.
790--
791--  The length field is actually 17 or 24 bits wide, depending on whether or
792--  not we're using raw, but that shouldn't matter.
793--
794--@param smb        The SMB object associated with the connection
795--@param header     The header, encoded with <code>smb_get_header</code>.
796--@param parameters The parameters.
797--@param data       The data.
798--@param overrides  Overrides table.
799--@return (result, err) If result is false, err is the error message. Otherwise, err is
800--        undefined
801function smb_send(smb, header, parameters, data, overrides)
802  overrides = overrides or {}
803
804  local encoded_parameters = smb_encode_parameters(parameters, overrides)
805  local encoded_data       = smb_encode_data(data, overrides)
806  local body               = header .. encoded_parameters .. encoded_data
807  local status, err
808
809  -- Calculate the message signature
810  body = message_sign(smb, body)
811
812  local out = string.pack(">s4", body)
813
814
815  stdnse.debug3("SMB: Sending SMB packet (len: %d)", #out)
816  status, err = smb['socket']:send(out)
817
818  if not status then
819    stdnse.debug1("SMB: Sending packet failed.")
820  end
821
822  return status, err
823end
824
825--- Reads the next packet from the socket, and parses it into the header, parameters,
826--  and data.
827--
828--@param smb The SMB object associated with the connection
829--@param read_data [optional] This function will read the data section if and only if
830--       this value is true. This is a workaround for a bug in the tree connect packet,
831--       where the length is set incorrectly. Default: true.
832--@return (status, header, parameters, data) If status is true, the header,
833--        parameters, and data are all the raw arrays (with the lengths already
834--        removed). If status is false, header contains an error message and parameters/
835--        data are undefined.
836function smb_read(smb, read_data)
837  local pos, netbios_data, netbios_length, length, header, parameter_length, parameters, data_length, data
838
839  stdnse.debug3("SMB: Receiving SMB packet")
840
841  -- Receive the response -- we make sure to receive at least 4 bytes, the length of the NetBIOS length
842  smb['socket']:set_timeout(TIMEOUT)
843
844  -- attempt to read the Netbios header
845  local status, netbios_data = smb['socket']:receive_buf(match.numbytes(4), true);
846
847  -- Make sure the connection is still alive
848  if not status then
849    return false, "SMB: Failed to receive bytes: " .. netbios_data
850  end
851
852  -- The length of the packet is 4 bytes of big endian (for our purposes).
853  -- The NetBIOS header is 24 bits, big endian
854  netbios_length, pos = string.unpack(">I4", netbios_data)
855  -- Make the length 24 bits
856  netbios_length = (netbios_length & 0x00FFFFFF)
857
858  -- The total length is the netbios_length, plus 4 (for the length itself)
859  length = netbios_length + 4
860
861  local status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true)
862
863  -- Make sure the connection is still alive
864  if not status then
865    return false, "SMB: Failed to receive bytes: " .. smb_data
866  end
867
868  local result = netbios_data .. smb_data
869  if(#result ~= length) then
870    stdnse.debug1("SMB: ERROR: Received wrong number of bytes, there will likely be issues (received %d, expected %d)", #result, length)
871    return false, string.format("SMB: ERROR: Didn't receive the expected number of bytes; received %d, expected %d. This will almost certainly cause some errors.", #result, length)
872  end
873
874  -- Check the message signature (ignoring the first four bytes, which are the netbios header)
875  local good_signature = message_check_signature(smb, string.sub(result, 5))
876  if(good_signature == false) then
877    return false, "SMB: ERROR: Server returned invalid signature"
878  end
879
880  local header_format = "<c32 B"
881  if (#result - pos + 1) < string.packsize(header_format) then
882    return false, "SMB: ERROR: Server returned less data than needed for header"
883  end
884  header, parameter_length, pos = string.unpack("<c32 B", result, pos)
885
886  -- Double the length parameter, since parameters are two-byte values.
887  if (length - pos + 1) < (parameter_length * 2) then
888    return false, "SMB: ERROR: parameter_length greater than response length"
889  end
890  parameters, pos = string.unpack(("<c%d"):format(parameter_length*2), result, pos)
891
892  -- The data length is a 2-byte value.
893  data_length, pos = string.unpack("<I2", result, pos)
894
895  -- Read that many bytes of data.
896  if(read_data == nil or read_data == true) then
897    if (length - pos + 1) < data_length then
898      return false, "SMB: ERROR: data_length greater than response length"
899    end
900    data = string.unpack("c" .. data_length, result, pos)
901  else
902    data = nil
903  end
904
905  stdnse.debug3("SMB: Received %d bytes", #result)
906  return true, header, parameters, data
907end
908
909---
910-- Negotiates SMBv1 connections
911--
912-- Sends the following:
913-- * List of known protocols
914--
915-- This function adds to <code>smb</code>:
916-- * 'security_mode'    Whether or not to use cleartext passwords, message signatures, etc.
917-- * 'max_mpx'          Maximum number of multiplexed connections
918-- * 'max_vc'           Maximum number of virtual circuits
919-- * 'max_buffer'       Maximum buffer size
920-- * 'max_raw_buffer'   Maximum buffer size for raw connections (considered obsolete)
921-- * 'session_key'      A value that's basically just echoed back
922-- * 'capabilities'     The server's capabilities
923-- * 'time'             The server's time (in UNIX-style seconds since 1970)
924-- * 'date'             The server's date in a user-readable format
925-- * 'timezone'         The server's timezone, in hours from UTC
926-- * 'timezone_str'     The server's timezone, as a string
927-- * 'server_challenge' A random string used for challenge/response
928-- * 'domain'           The server's primary domain or workgroup
929-- * 'server'           The server's name
930--
931-- @param smb The SMB object associated with the connection.
932-- @param overrides Overrides table.
933-- @return Boolean status
934-- @return The negotiated dialect in human readable form or an error message.
935---
936function negotiate_v1(smb, overrides)
937  local header = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'], overrides)
938  -- Make sure we have overrides
939  overrides = overrides or {}
940
941  -- Parameters are blank
942  local parameters = ""
943  local data = string.pack("<BzBz", 2, (overrides['dialect'] or "NT LM 0.12"), 2, "")
944
945  -- Send the negotiate request
946  stdnse.debug2("SMB: Sending SMB_COM_NEGOTIATE")
947  local result, err = smb_send(smb, header, parameters, data, overrides)
948  if(result == false) then
949    return false, err
950  end
951  -- Read the result
952  local status, header, parameters, data = smb_read(smb)
953  if(status ~= true) then
954    return false, header
955  end
956
957  -- Check if we fell off the packet
958  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
959  if #header < string.packsize(header_format) then
960    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
961  end
962
963  -- Parse out the header
964  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
965
966  -- Get the protocol version
967  if(protocol_version == ("\xFESMB")) then
968    return false, "SMB: Server returned a SMBv2 packet, don't know how to handle"
969  end
970
971  -- Since this is the first response seen, check any necessary flags here
972  if((flags2 & 0x0800) ~= 0x0800) then
973    smb['extended_security'] = false
974  end
975
976  -- Parse the parameter section
977  local dialect_format = "<I2"
978  local parameters_format = "<BI2 I2 I4 I4 I4 I4"
979  if #parameters < (string.packsize(dialect_format) + string.packsize(parameters_format)) then
980    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [9]"
981  end
982  smb['dialect'], pos = string.unpack(dialect_format, parameters)
983
984  -- Check if the server didn't like our requested protocol
985  if(smb['dialect'] ~= 0) then
986    stdnse.debug2("Server negotiated an unknown protocol (#%d) -- aborting", smb['dialect'])
987    return false, string.format("Server negotiated an unknown protocol (#%d) -- aborting", smb['dialect'])
988  end
989
990  smb.security_mode, smb.max_mpx, smb.max_vc, smb.max_buffer, smb.max_raw_buffer, smb.session_key, smb.capabilities, pos = string.unpack(parameters_format, parameters, pos)
991  -- Some broken implementations of SMB don't send these variables
992  smb.time = 0
993  smb.timezone = 0
994  smb.key_length = 0
995  smb.byte_count = 0
996  if (#parameters - pos + 1) >= 8 then
997    smb.time, pos = string.unpack("<I8", parameters, pos)
998    if (#parameters - pos + 1) >= 2 then
999      smb.timezone, pos = string.unpack("<i2", parameters, pos)
1000      if (#parameters - pos + 1) >= 1 then
1001        smb.key_length, pos = string.unpack("B", parameters, pos)
1002        if (#parameters - pos + 1) >= 2 then
1003          smb.byte_count, pos = string.unpack("<I2", parameters, pos)
1004        end
1005      end
1006    end
1007  end
1008
1009  -- Convert the time and timezone to more useful values
1010  smb['time'] = (smb['time'] // 10000000) - 11644473600
1011  smb['date'] = datetime.format_timestamp(smb['time'])
1012  smb['timezone'] = -(smb['timezone'] / 60)
1013  if(smb['timezone'] == 0) then
1014    smb['timezone_str'] = "UTC+0"
1015  elseif(smb['timezone'] < 0) then
1016    smb['timezone_str'] = "UTC-" .. math.abs(smb['timezone'])
1017  else
1018    smb['timezone_str'] = "UTC+" .. smb['timezone']
1019  end
1020
1021  -- Data section
1022  if(smb['extended_security'] == true) then
1023    if #data < 16 then
1024      return false, "SMB: ERROR: not enough data for extended security"
1025    end
1026    smb.server_guid, pos = string.unpack("<c16", data)
1027
1028    -- do we have a security blob?
1029    if ( #data - pos + 1 > 0 ) then
1030      smb.security_blob = data:sub(pos)
1031      pos = #data + 1
1032    end
1033  else
1034    if #data < smb.key_length then
1035      return false, "SMB: ERROR: not enough data for server_challenge"
1036    end
1037    smb.server_challenge, pos = string.unpack(string.format("<c%d", smb['key_length']), data)
1038
1039    -- Get the (null-terminated) domain as a Unicode string
1040    smb['domain'] = ""
1041    smb['server'] = ""
1042
1043    local remainder = unicode.utf16to8(string.sub(data, pos))
1044    pos, pos = string.find(remainder, "\0", 1, true)
1045    if pos == nil then
1046      return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [14]"
1047    end
1048    smb['domain'] = string.sub(remainder, 1, pos)
1049
1050    -- Get the server name as a Unicode string
1051    -- Note: This can be nil, Samba leaves this off
1052    local pos2 = pos + 1
1053    pos, pos = string.find(remainder, "\0", pos2, true)
1054    if pos ~= nil then
1055      smb['server'] = string.sub(remainder, pos2, pos)
1056    end
1057  end
1058
1059  stdnse.debug2("SMB_COM_NEGOTIATE got status:%s", status)
1060  if status == 0 then
1061    return true, overrides['dialect'] or "NT LM 0.12"
1062  end
1063end
1064
1065---
1066-- Wrapper function to negotiate the protocol to use in the SMB connection.
1067-- By default it attempts to negotiate with using following dialects:
1068-- * NT LM 12.0 (SMBv1)
1069-- @param smb The SMB object
1070-- @param overrides Overrides table
1071-- @return Boolean status
1072---
1073function negotiate_protocol(smb, overrides)
1074  local status, dialect
1075  status, dialect = negotiate_v1(smb, overrides)
1076  if status then
1077    return true
1078  else
1079    stdnse.debug1("Couldn't negotiate a SMBv1 connection:%s", dialect)
1080    return false, string.format("Could not negotiate a connection:%s", dialect)
1081  end
1082end
1083
1084---
1085-- Returns list of supported dialects for SMBv1, SMBv2 and SMBv3.
1086-- @param host       The SMB host to connect to.
1087-- @param overrides [optional] Overrides for various fields.
1088-- @return Boolean status
1089-- @return Table of supported dialects or error message
1090---
1091function list_dialects(host, overrides)
1092  local smb2_dialects = {0x0202, 0x0210, 0x0300, 0x0302, 0x0311}
1093  local supported_dialects = {}
1094  local status, smb1_dialect
1095  local smbstate
1096
1097  overrides = tableaux.tcopy(overrides or {})
1098
1099  -- Check for SMBv1 first
1100  stdnse.debug2("Checking if SMBv1 is supported")
1101  status, smbstate = start(host)
1102  if(status == false) then
1103    return false, smbstate
1104  end
1105
1106  status, smb1_dialect = negotiate_v1(smbstate, overrides)
1107  if status then --Add SMBv1 as a dialect
1108    table.insert(supported_dialects, smb1_dialect)
1109  end
1110  stop(smbstate)
1111  status = false -- Finish SMBv1 and close connection
1112
1113  -- Check SMB2 and SMB3 dialects
1114  for i, dialect in pairs(smb2_dialects) do
1115    local dialect_human = stdnse.tohex(dialect, {separator = ".", group = 2})
1116    -- we need a clean connection for each negotiate request
1117    status, smbstate = start(host)
1118    if(status == false) then
1119      return false, smbstate
1120    end
1121    stdnse.debug2("Checking if dialect '%s' is supported", dialect_human)
1122    overrides['Dialects'] = {dialect}
1123    status, dialect = smb2.negotiate_v2(smbstate, overrides)
1124    if status then
1125      stdnse.debug2("SMB2: Dialect '%s' is supported", dialect_human)
1126      table.insert(supported_dialects, dialect_human)
1127    end
1128    --clean smb connection
1129    stop(smbstate)
1130    status = false
1131  end
1132
1133  return true, supported_dialects
1134end
1135
1136--- This is an internal function and should not be called externally. Use
1137--  the start_session() function instead.
1138local function start_session_basic(smb, log_errors, overrides)
1139  local i, err
1140  local status, result
1141  local header, parameters, data, domain
1142  local andx_command, andx_reserved, andx_offset, action
1143  local os, lanmanager
1144  local username, domain, password, password_hash, hash_type
1145  local busy_count = 0
1146
1147  header = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'], overrides)
1148
1149  -- Get the first account, unless they overrode it
1150  if(overrides ~= nil and overrides['username'] ~= nil) then
1151    result = true
1152    username      = overrides['username']
1153    domain        = overrides['domain']
1154    password      = overrides['password']
1155    password_hash = overrides['password_hash']
1156    hash_type     = overrides['hash_type']
1157  else
1158    result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
1159  end
1160
1161  while result ~= false do
1162    local lanman, ntlm
1163
1164    lanman, ntlm, smb['mac_key'] = smbauth.get_password_response(smb['ip'], username, domain, password, password_hash, hash_type, smb['server_challenge'], false)
1165
1166    -- Parameters
1167    parameters = string.pack("<BBI2 I2I2 I2 I4 I2I2 I4I4",
1168      0xFF,               -- ANDX -- no further commands
1169      0x00,               -- ANDX -- Reserved (0)
1170      0x0000,             -- ANDX -- next offset
1171      0xFFFF,             -- Max buffer size
1172      0x0001,             -- Max multiplexes
1173      0x0001,             -- Virtual circuit num
1174      smb['session_key'], -- The session key
1175      #lanman,            -- ANSI/Lanman password length
1176      #ntlm,              -- Unicode/NTLM password length
1177      0x00000000,         -- Reserved
1178      0x00000050          -- Capabilities
1179      )
1180
1181    -- Data is a list of strings, terminated by a blank one.
1182    data = lanman -- ANSI/Lanman password
1183    .. ntlm -- Unicode/NTLM password
1184    .. string.pack("<zzzz",
1185      username,               -- Account
1186      domain,                 -- Domain
1187      "Nmap",                 -- OS
1188      "Native Lanman"         -- Native LAN Manager
1189      )
1190
1191    -- Send the session setup request
1192    stdnse.debug2("SMB: Sending SMB_COM_SESSION_SETUP_ANDX")
1193    result, err = smb_send(smb, header, parameters, data, overrides)
1194    if(result == false) then
1195      return false, err
1196    end
1197
1198    -- Read the result
1199    status, header, parameters, data = smb_read(smb)
1200    if(status ~= true) then
1201      return false, header
1202    end
1203
1204    local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1205    if #header < string.packsize(header_format) then
1206      return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [17]"
1207    end
1208    -- Check if we were allowed in
1209    local protocol_version, command, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos
1210    protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1211
1212    -- Check if we're successful
1213    if(status == 0) then
1214
1215      -- Parse the parameters
1216      local parameters_format = "<BB I2 I2"
1217      if #parameters < string.packsize(parameters_format) then
1218        return false, "SMB: ERROR: Server returned less data than needed"
1219      end
1220      andx_command, andx_reserved, andx_offset, action, pos = string.unpack(parameters_format, parameters)
1221
1222      -- Parse the data
1223      status, os, lanmanager, domain, pos = pcall(string.unpack, "<zzz", data)
1224      if not status then
1225        return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [19]"
1226      end
1227
1228      -- Fill in the smb object and smb string
1229      smb['uid']        = uid
1230      smb['is_guest']   = (action & 1)
1231      smb['os']         = os
1232      smb['lanmanager'] = lanmanager
1233
1234      -- Check if they're using an un-supported system
1235      if(os == "" or lanmanager == "" or domain == "") then
1236        stdnse.debug1("SMB: WARNING: the server is using a non-standard SMB implementation; your mileage may vary (%s)", smb['ip'])
1237      elseif(os == "Unix" or string.sub(lanmanager, 1, 5) == "Samba") then
1238        stdnse.debug1("SMB: WARNING: the server appears to be Unix; your mileage may vary.")
1239      end
1240
1241      -- Check if they were logged in as a guest
1242      if(log_errors == nil or log_errors == true) then
1243        if(smb['is_guest'] == 1) then
1244          stdnse.debug1("SMB: Login as %s\\%s failed, but was given guest access (username may be wrong, or system may only allow guest)", domain, stdnse.string_or_blank(username))
1245        else
1246          stdnse.debug2("SMB: Login as %s\\%s succeeded", domain, stdnse.string_or_blank(username))
1247        end
1248      end
1249
1250      -- Set the initial sequence number
1251      smb['sequence'] = 1
1252
1253      return true
1254
1255    else
1256      -- Check if we got the error NT_STATUS_REQUEST_NOT_ACCEPTED
1257      if(status == 0xc00000d0) then
1258        busy_count = busy_count + 1
1259
1260        if(busy_count > 9) then
1261          return false, "SMB: ERROR: Server has too many active connections; giving up."
1262        end
1263
1264        local backoff = math.random() * 10
1265        stdnse.debug1("SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100)
1266        stdnse.sleep(backoff)
1267      else
1268        -- This username failed, print a warning and keep going
1269        if(log_errors == nil or log_errors == true) then
1270          stdnse.debug1("SMB: Login as %s\\%s failed (%s)", domain, stdnse.string_or_blank(username), get_status_name(status))
1271        end
1272
1273        -- Go to the next account
1274        if(overrides == nil or overrides['username'] == nil) then
1275          smbauth.next_account(smb['host'])
1276          result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
1277        else
1278          result = false
1279        end
1280      end
1281    end
1282  end
1283
1284  if(log_errors ~= false) then
1285    stdnse.debug1("SMB: ERROR: %s", username)
1286  end
1287
1288  if (status ~= nil) then
1289    return false, get_status_name(status)
1290  else
1291    return false, username
1292  end
1293end
1294
1295--- This is an internal function and should not be called externally. Use
1296--  the start_session() function instead.
1297local function start_session_extended(smb, log_errors, overrides)
1298  local i
1299  local status, status_name, result, err
1300  local header, parameters, data
1301  local andx_command, andx_reserved, andx_offset, action, security_blob_length
1302  local os, lanmanager
1303  local username, domain, password, password_hash, hash_type
1304  local busy_count = 0
1305
1306  -- Set a default status_name, in case everything fails
1307  status_name = "An unknown error has occurred"
1308
1309  -- Get the first account, unless they overrode it
1310  if(overrides ~= nil and overrides['username'] ~= nil) then
1311    result = true
1312    username      = overrides['username']
1313    domain        = overrides['domain']
1314    password      = overrides['password']
1315    password_hash = overrides['password_hash']
1316    hash_type     = overrides['hash_type']
1317  else
1318    result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
1319    if(not(result)) then
1320      return result, username
1321    end
1322  end
1323
1324  -- check what kind of security blob we were given in the negotiate protocol request
1325  local sp_nego = false
1326  if ( smb['security_blob'] and #smb['security_blob'] > 11 ) then
1327    local oid, pos = string.unpack(">c6", smb['security_blob'], 5)
1328    sp_nego = ( oid == "\x2b\x06\x01\x05\x05\x02" or oid == "\x06\x06\x2b\x06\x01\x05" ) -- check for SPNEGO OID 1.3.6.1.5.5.2
1329  end
1330
1331  local ntlm_challenge_accepted = false
1332  while result ~= false do
1333    -- These are loop variables
1334    local security_blob = nil
1335    local security_blob_length = 0
1336
1337    -- This loop takes care of the multiple packets that "extended security" requires
1338    repeat
1339      -- Get the new security blob, passing the old security blob as a parameter. If there was no previous security blob, then nil is passed, which creates a new one
1340      if ( not(security_blob) ) then
1341        status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215))
1342
1343        if ( sp_nego ) then
1344          local enc = asn1.ASN1Encoder:new()
1345          local mechtype = enc:encode( { type = 'A0', value = enc:encode( { type = '30', value = enc:encode( { type = '06', value = stdnse.fromhex("2b06010401823702020a") } ) } ) } )
1346          local oid = enc:encode( { type = '06', value = stdnse.fromhex("2b0601050502") } )
1347
1348          security_blob = enc:encode(security_blob)
1349          security_blob = enc:encode( { type = 'A2', value = security_blob } )
1350          security_blob = mechtype .. security_blob
1351          security_blob = enc:encode( { type = '30', value = security_blob } )
1352          security_blob = enc:encode( { type = 'A0', value = security_blob } )
1353          security_blob = oid .. security_blob
1354          security_blob = enc:encode( { type = '60', value = security_blob } )
1355        end
1356      else
1357        if ( sp_nego ) then
1358          if (smb['domain'] or smb['server']) and (not domain or #domain == 0) then
1359            domain = smb['domain'] or smb['server']
1360          end
1361          hash_type = "ntlm"
1362        end
1363
1364        status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215))
1365
1366        if ( sp_nego ) then
1367          local enc = asn1.ASN1Encoder:new()
1368          security_blob = enc:encode(security_blob)
1369          security_blob = enc:encode( { type = 'A2', value = security_blob } )
1370          security_blob = enc:encode( { type = '30', value = security_blob } )
1371          security_blob = enc:encode( { type = 'A1', value = security_blob } )
1372        end
1373
1374      end
1375
1376      -- There was an error processing the security blob
1377      if(status == false) then
1378        return false, string.format("SMB: ERROR: Security blob: %s", security_blob)
1379      end
1380
1381      header     = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'], overrides)
1382
1383      -- Data is a list of strings, terminated by a blank one.
1384      data = security_blob -- Security blob
1385      .. string.pack("<zzz",
1386        "Nmap",                -- OS
1387        "Native Lanman",       -- Native LAN Manager
1388        ""                     -- Primary domain
1389        )
1390
1391      -- Parameters
1392      parameters = string.pack("<BB I2 I2 I2 I2 I4 I2 I4 I4",
1393        0xFF,               -- ANDX -- no further commands
1394        0x00,               -- ANDX -- Reserved (0)
1395        #data + 24 + #header + 3, -- ANDX -- next offset
1396        0xFFFF,             -- Max buffer size
1397        0x0001,             -- Max multiplexes
1398        0x0001,             -- Virtual circuit num
1399        smb['session_key'], -- The session key
1400        #security_blob,     -- Security blob length
1401        0x00000000,         -- Reserved
1402        0x80000050          -- Capabilities
1403        )
1404
1405      -- Send the session setup request
1406      stdnse.debug2("SMB: Sending SMB_COM_SESSION_SETUP_ANDX")
1407      result, err = smb_send(smb, header, parameters, data, overrides)
1408      if(result == false) then
1409        return false, err
1410      end
1411
1412      -- Read the result
1413      status, header, parameters, data = smb_read(smb)
1414      if(status ~= true) then
1415        return false, header
1416      end
1417
1418      local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1419      if #header < string.packsize(header_format) then
1420        return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
1421      end
1422
1423      -- Check if we were allowed in
1424      local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1425      smb['uid'] = uid
1426
1427      -- Get a human readable name
1428      status_name = get_status_name(status)
1429
1430      -- Only parse the parameters if it's ok or if we're going to keep going
1431      if(status_name == "NT_STATUS_SUCCESS" or status_name == "NT_STATUS_MORE_PROCESSING_REQUIRED") then
1432        -- Parse the parameters
1433        local parameters_format = "<BBI2 I2 I2"
1434        if #parameters < string.packsize(parameters_format) then
1435          return false, "SMB: ERROR: Server returned less data than needed"
1436        end
1437        andx_command, andx_reserved, andx_offset, action, security_blob_length, pos = string.unpack(parameters_format, parameters)
1438        smb['is_guest']   = (action & 1)
1439
1440        -- Parse the data
1441        if #data < security_blob_length then
1442          return false, "SMB: ERROR: Server returned less data than needed"
1443        end
1444        security_blob, pos = string.unpack(("<c%d"):format(security_blob_length), data)
1445        status, os, lanmanager, pos = pcall(string.unpack, "zz", data, pos)
1446
1447        if not ntlm_challenge_accepted then
1448          if ( status_name == "NT_STATUS_MORE_PROCESSING_REQUIRED" and sp_nego ) then
1449            local start = security_blob:find("NTLMSSP")
1450            security_blob = security_blob:sub(start)
1451          end
1452
1453          if not status or security_blob == nil then
1454            return false, "SMB: ERROR: NTLM challenge not accepted or lanmanager missing"
1455          end
1456          smb['os']         = os
1457          smb['lanmanager'] = lanmanager
1458
1459          local host_info = smbauth.get_host_info_from_security_blob(security_blob)
1460          if ( host_info ) then
1461            smb['fqdn'] = host_info['fqdn']
1462            smb['domain_dns'] = host_info['dns_domain_name']
1463            smb['forest_dns'] = host_info['dns_forest_name']
1464            smb['server'] = host_info['netbios_computer_name']
1465            smb['domain'] = host_info['netbios_domain_name']
1466          end
1467          ntlm_challenge_accepted = true
1468        end
1469
1470
1471        -- If it's ok, do a cleanup and return true
1472        if(status_name == "NT_STATUS_SUCCESS") then
1473          -- Check if they're using an un-supported system
1474          if not status then
1475            stdnse.debug1("SMB: WARNING: the server is using a non-standard SMB implementation; your mileage may vary (%s)", smb['ip'])
1476          elseif(os == "Unix" or string.sub(lanmanager, 1, 5) == "Samba") then
1477            stdnse.debug1("SMB: WARNING: the server appears to be Unix; your mileage may vary.")
1478          end
1479
1480          -- Check if they were logged in as a guest
1481          if(log_errors == nil or log_errors == true) then
1482            if(smb['is_guest'] == 1) then
1483              stdnse.debug1("SMB: Extended login to %s as %s\\%s failed, but was given guest access (username may be wrong, or system may only allow guest)", smb['ip'], domain, stdnse.string_or_blank(username))
1484            else
1485              stdnse.debug2("SMB: Extended login to %s as %s\\%s succeeded", smb['ip'], domain, stdnse.string_or_blank(username))
1486            end
1487          end
1488
1489          -- Set the initial sequence number
1490          smb['sequence'] = 1
1491
1492          return true
1493        end -- Status is ok
1494      end -- Should we parse the parameters/data?
1495    until status_name ~= "NT_STATUS_MORE_PROCESSING_REQUIRED"
1496
1497    -- Check if we got the error NT_STATUS_REQUEST_NOT_ACCEPTED
1498    if(status == 0xc00000d0) then
1499      busy_count = busy_count + 1
1500
1501      if(busy_count > 9) then
1502        return false, "SMB: ERROR: Server has too many active connections; giving up."
1503      end
1504
1505      local backoff = math.random() * 10
1506      stdnse.debug1("SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100)
1507      stdnse.sleep(backoff)
1508    else
1509      -- Display a message to the user, and try the next account
1510      if(log_errors == nil or log_errors == true) then
1511        stdnse.debug1("SMB: Extended login to %s as %s\\%s failed (%s)", smb['ip'], domain, stdnse.string_or_blank(username), status_name)
1512      end
1513
1514      -- Go to the next account
1515      if(overrides == nil or overrides['username'] == nil) then
1516        smbauth.next_account(smb['host'])
1517        result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
1518        if(not(result)) then
1519          return false, username
1520        end
1521      else
1522        result = false
1523      end
1524    end
1525
1526    -- Reset the user id
1527    smb['uid'] = 0
1528
1529  end -- Loop over the accounts
1530
1531  if(log_errors == nil or log_errors == true) then
1532    stdnse.debug1("SMB: ERROR: All logins failed, sorry it didn't work out!")
1533  end
1534
1535  return false, status_name
1536end
1537
1538--- Sends out SMB_COM_SESSION_SETUP_ANDX, which attempts to log a user in.
1539--
1540-- Sends the following:
1541-- * Negotiated parameters (multiplexed connections, virtual circuit, capabilities)
1542-- * Passwords (plaintext, unicode, lanman, ntlm, lmv2, ntlmv2, etc)
1543-- * Account name
1544-- * OS (I just send "Nmap")
1545-- * Native LAN Manager (no clue what that is, but it seems to be ignored)
1546--
1547-- Receives the following:
1548-- * User ID
1549-- * Server OS
1550--
1551--@param smb          The SMB object associated with the connection
1552--@param overrides    [optional] A table of overrides for username, domain, password, password_hash, and hash_type.
1553--                    If any of these are given, it's used first. If they aren't, then Nmap parameters, Nmap registry entries,
1554--                    guest, and NULL sessions are used.
1555--@param log_errors   [optional] If set, will display login. Default: true.
1556--@return (status, result) If status is false, result is an error message. Otherwise, result is nil and the following
1557--        elements are added to the smb table:
1558--    *  'uid'         The UserID for the session
1559--    *  'is_guest'    If set, the username wasn't found so the user was automatically logged in as the guest account
1560--    *  'os'          The operating system
1561--    *  'lanmanager'  The server's LAN Manager
1562function start_session(smb, overrides, log_errors)
1563  -- Use a mutex to avoid some issues (see http://seclists.org/nmap-dev/2011/q1/464)
1564  local smb_auth_mutex = nmap.mutex( "SMB Authentication Mutex" )
1565  smb_auth_mutex( "lock" )
1566
1567  local status, result
1568  if(smb['extended_security'] == true) then
1569    status, result = start_session_extended(smb, log_errors, overrides)
1570  else
1571    status, result = start_session_basic(smb, log_errors, overrides)
1572  end
1573
1574  smb_auth_mutex( "done" )
1575  return status, result
1576end
1577
1578--- Sends out <code>SMB_COM_SESSION_TREE_CONNECT_ANDX</code>, which attempts to
1579-- connect to a share.
1580--
1581-- Sends the following:
1582-- * Password (for share-level security, which we don't support)
1583-- * Share name
1584-- * Share type (or "?????" if it's unknown, that's what we do)
1585--
1586-- Receives the following:
1587-- * Tree ID
1588--
1589--@param smb       The SMB object associated with the connection
1590--@param path      The path to connect (eg, <code>"\\servername\C$"</code>)
1591--@param overrides [optional] Overrides for various fields
1592--@return (status, result) If status is false, result is an error message. Otherwise, result is a
1593--        table with the following elements:
1594--      * 'tid'         The TreeID for the session
1595function tree_connect(smb, path, overrides)
1596  local header, parameters, data, err, result
1597  local andx_command, andx_reserved, andx_offset, action
1598  local status
1599
1600  -- Make sure we have overrides
1601  overrides = overrides or {}
1602
1603  header = smb_encode_header(smb, command_codes['SMB_COM_TREE_CONNECT_ANDX'], overrides)
1604  parameters = string.pack("<BBI2 I2 I2",
1605    0xFF,   -- ANDX no further commands
1606    0x00,   -- ANDX reserved
1607    0x0000, -- ANDX offset
1608    (overrides['tree_connect_flags'] or 0x0000), -- flags
1609    0x0000 -- password length (for share-level security)
1610    )
1611  data = string.pack("zz",
1612    -- Share-level password
1613    path,   -- Path
1614    (overrides['tree_type'] or "?????") -- Type of tree ("?????" = any)
1615    )
1616
1617  -- Send the tree connect request
1618  stdnse.debug2("SMB: Sending SMB_COM_TREE_CONNECT_ANDX")
1619  result, err = smb_send(smb, header, parameters, data, overrides)
1620  if(result == false) then
1621    return false, err
1622  end
1623
1624  -- Read the result
1625  status, header, parameters, data = smb_read(smb)
1626  if(status ~= true) then
1627    return false, header
1628  end
1629
1630  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1631  if #header < string.packsize(header_format) then
1632    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
1633  end
1634  -- Check if we were allowed in
1635  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1636
1637  if(status ~= 0) then
1638    return false, get_status_name(status)
1639  end
1640
1641  if(tid == 0 or tonumber(tid) == 0) then
1642    return false, "SMB: ERROR: Server didn't establish a proper tree connection (likely an embedded system)"
1643  end
1644
1645  smb['tid'] = tid
1646
1647  return true
1648
1649end
1650
1651--- Disconnects a tree session. Should be called before logging off and disconnecting.
1652--@param smb    The SMB object associated with the connection
1653--@param overrides THe overrides table
1654--@return (status, result) If status is false, result is an error message. If status is true,
1655--              the disconnect was successful.
1656function tree_disconnect(smb, overrides)
1657  overrides = overrides or {}
1658  local header
1659
1660  header = smb_encode_header(smb, command_codes['SMB_COM_TREE_DISCONNECT'], overrides)
1661
1662  -- Send the tree disconnect request
1663  stdnse.debug2("SMB: Sending SMB_COM_TREE_DISCONNECT")
1664  local result, err = smb_send(smb, header, "", "", overrides)
1665  if(result == false) then
1666    return false, err
1667  end
1668
1669  -- Read the result
1670  local status, header, parameters, data = smb_read(smb)
1671  if(status ~= true) then
1672    return false, header
1673  end
1674
1675  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1676  if #header < string.packsize(header_format) then
1677    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
1678  end
1679
1680  -- Check if there was an error
1681  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1682
1683  if(status ~= 0) then
1684    return false, get_status_name(status)
1685  end
1686
1687  smb['tid'] = 0
1688
1689  return true
1690
1691end
1692
1693---Logs off the current user. Strictly speaking this isn't necessary, but it's the polite thing to do.
1694--
1695--@param smb    The SMB object associated with the connection
1696--@param overrides THe overrides table
1697--@return (status, result) If status is false, result is an error message. If status is true,
1698--              the logoff was successful.
1699function logoff(smb, overrides)
1700  overrides = overrides or {}
1701  local header, parameters, data
1702  local status
1703
1704  header = smb_encode_header(smb, command_codes['SMB_COM_LOGOFF_ANDX'], overrides)
1705
1706  -- Parameters are a blank ANDX block
1707  parameters = string.pack("<BB I2",
1708    0xFF,   -- ANDX no further commands
1709    0x00,   -- ANDX reserved
1710    0x0000  -- ANDX offset
1711    )
1712
1713  -- Send the tree disconnect request
1714  stdnse.debug2("SMB: Sending SMB_COM_LOGOFF_ANDX")
1715  local result, err = smb_send(smb, header, parameters, "", overrides)
1716  if(result == false) then
1717    return false, err
1718  end
1719
1720  -- Read the result
1721  status, header, parameters, data = smb_read(smb)
1722  if(status ~= true) then
1723    return false, header
1724  end
1725
1726  -- Reset session variables (note: this has to come after the smb_read(), otherwise the message signatures cause a problem
1727  smb['uid']      = 0
1728  smb['sequence'] = -1
1729  smb['mac_key']  = nil
1730
1731  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1732  if #header < string.packsize(header_format) then
1733    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [22]"
1734  end
1735
1736  -- Check if there was an error
1737  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1738
1739  if(status == 0xc0000022) then
1740    stdnse.debug1("SMB: ERROR: Access was denied in 'logoff', indicating a problem with your message signatures")
1741    return false, "SMB: ERROR: Access was denied in 'logoff', indicating a problem with your message signatures"
1742  end
1743  if(status ~= 0) then
1744    return false, get_status_name(status)
1745  end
1746
1747  return true
1748
1749end
1750
1751--- This sends a SMB request to open or create a file.
1752--
1753--  Most of the parameters I pass here are used directly from a packetlog,
1754--  especially the various permissions fields and flags.  I might make this
1755--  more adjustable in the future, but this has been working for me.
1756--
1757--@param smb       The SMB object associated with the connection
1758--@param path      The path of the file or pipe to open
1759--@param overrides [optional] Overrides for various fields
1760--@return (status, result) If status is false, result is an error message. Otherwise, result is a table
1761--        containing a lot of different elements, the most important one being 'fid', the handle to the opened file.
1762function create_file(smb, path, overrides)
1763  local header, parameters, data
1764  local andx_command, andx_reserved, andx_offset
1765  local oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory
1766  local error_count = 0
1767
1768  local status, pos
1769  repeat
1770    local mutex = nmap.mutex(smb['host'])
1771    mutex "lock"
1772
1773    -- Make sure we have overrides
1774    overrides = overrides or {}
1775
1776    header = smb_encode_header(smb, command_codes['SMB_COM_NT_CREATE_ANDX'], overrides)
1777    parameters = string.pack("<BBI2 B I2 I4 I4 I4 I8 I4 I4 I4 I4 I4 B",
1778      0xFF,   -- ANDX no further commands
1779      0x00,   -- ANDX reserved
1780      0x0000, -- ANDX offset
1781      0x00,   -- Reserved
1782      #path, -- Path length
1783      (overrides['file_create_flags']            or 0x00000016),         -- Create flags
1784      (overrides['file_create_root_fid']         or 0x00000000),         -- Root FID
1785      (overrides['file_create_access_mask']      or 0x02000000),         -- Access mask
1786      (overrides['file_create_allocation_size']  or 0x0000000000000000), -- Allocation size
1787      (overrides['file_create_attributes']       or 0x00000000),         -- File attributes
1788      (overrides['file_create_share_attributes'] or 0x00000007),         -- Share attributes
1789      (overrides['file_create_disposition']      or 0x00000000),         -- Disposition
1790      (overrides['file_create_options']          or 0x00000000),         -- Create options
1791      (overrides['file_create_impersonation']    or 0x00000002),         -- Impersonation
1792      (overrides['file_create_security_flags']   or 0x01)                -- Security flags
1793      )
1794
1795    data = string.pack("z", path)
1796
1797    -- Send the create file
1798    stdnse.debug2("SMB: Sending SMB_COM_NT_CREATE_ANDX")
1799    local result, err = smb_send(smb, header, parameters, data, overrides)
1800    if(result == false) then
1801      mutex "done"
1802      return false, err
1803    end
1804
1805    -- Read the result
1806    status, header, parameters, data = smb_read(smb, false)
1807    mutex "done"
1808    if(status ~= true) then
1809      return false, header
1810    end
1811
1812    local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1813    if #header < string.packsize(header_format) then
1814      return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [23]"
1815    end
1816
1817    -- Check if we were allowed in
1818    local protocol_version, command, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid
1819    protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1820
1821    if(status == 0xc00000ac) then
1822      error_count = error_count + 1
1823      if(error_count > 10) then
1824        return false, "SMB: ERROR: Server returned NT_STATUS_PIPE_NOT_AVAILABLE too many times; giving up."
1825      end
1826      stdnse.debug1("WARNING: Server refused connection with NT_STATUS_PIPE_NOT_AVAILABLE; trying again")
1827      stdnse.sleep(.2)
1828    end
1829  until (status ~= 0xc00000ac)
1830
1831  if(status ~= 0) then
1832    return false, get_status_name(status)
1833  end
1834
1835  -- Parse the parameters
1836  local parameters_format = "<BBI2 BI2 I4 I8 I8 I8 I8 I4 I8 I8 I2 I2 B"
1837  if #parameters < string.packsize(parameters_format) then
1838    return false, "SMB: ERROR: Server returned less data than needed"
1839  end
1840  andx_command, andx_reserved, andx_offset, oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory, pos = string.unpack(parameters_format, parameters)
1841
1842  -- Fill in the smb table
1843  smb['oplock_level']    = oplock_level
1844  smb['fid']             = fid
1845  smb['create_action']   = create_action
1846  smb['created']         = created
1847  smb['last_access']     = last_access
1848  smb['last_write']      = last_write
1849  smb['last_change']     = last_change
1850  smb['attributes']      = attributes
1851  smb['allocation_size'] = allocation_size
1852  smb['end_of_file']     = end_of_file
1853  smb['filetype']        = filetype
1854  smb['ipc_state']       = ipc_state
1855  smb['is_directory']    = is_directory
1856
1857  return true
1858end
1859
1860--- This sends a SMB request to read from a file (or a pipe).
1861--
1862--@param smb    The SMB object associated with the connection
1863--@param offset The offset to read from (ignored if it's a pipe)
1864--@param count  The maximum number of bytes to read
1865--@param overrides The overrides table
1866--@return (status, result) If status is false, result is an error message. Otherwise, result is a table
1867--        containing a lot of different elements.
1868function read_file(smb, offset, count, overrides)
1869  overrides = overrides or {}
1870  local header, parameters, data
1871  local andx_command, andx_reserved, andx_offset
1872  local remaining, data_compaction_mode, reserved_1, data_length_low, data_offset, data_length_high, reserved_2, reserved_3
1873  local response = {}
1874  local status
1875
1876  header = smb_encode_header(smb, command_codes['SMB_COM_READ_ANDX'], overrides)
1877  parameters = string.pack("<BBI2 I2 I4 I2 I2 I4 I2 I4",
1878    0xFF,   -- ANDX no further commands
1879    0x00,   -- ANDX reserved
1880    0x0000, -- ANDX offset
1881    smb['fid'], -- FID
1882    offset,     -- Offset
1883    count,      -- Max count low
1884    count,      -- Min count
1885    0xFFFFFFFF, -- Reserved
1886    0,          -- Remaining
1887    0x00000000  -- High offset
1888    )
1889
1890  data = ""
1891
1892  -- Send the create file
1893  stdnse.debug2("SMB: Sending SMB_COM_READ_ANDX")
1894  local result, err = smb_send(smb, header, parameters, data, overrides)
1895  if(result == false) then
1896    return false, err
1897  end
1898
1899  -- Read the result
1900  status, header, parameters, data = smb_read(smb)
1901  if(status ~= true) then
1902    return false, header
1903  end
1904
1905  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
1906  if #header < string.packsize(header_format) then
1907    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [25]"
1908  end
1909
1910  -- Check if we were allowed in
1911  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
1912
1913  if(status ~= 0 and
1914      (status ~= status_codes.NT_STATUS_BUFFER_OVERFLOW and (smb['filetype'] == filetype_codes.FILE_TYPE_BYTE_MODE_PIPE or
1915      smb['filetype'] == filetype_codes.FILE_TYPE_MESSAGE_MODE_PIPE) ) ) then
1916    return false, get_status_name(status)
1917  end
1918
1919  -- Parse the parameters
1920  local parameters_format = "<BBI2 I2 I2 I2 I2 I2 I4 I2 I4"
1921  if #parameters < string.packsize(parameters_format) then
1922    return false, "SMB: ERROR: Server returned less data than needed"
1923  end
1924  andx_command, andx_reserved, andx_offset, remaining, data_compaction_mode, reserved_1, data_length_low, data_offset, data_length_high, reserved_2, reserved_3, pos = string.unpack(parameters_format, parameters)
1925
1926  response['remaining']   = remaining
1927  response['data_length'] = (data_length_low | (data_length_high << 16))
1928  response['status']      = status
1929
1930
1931  -- data_start is the offset of the beginning of the data section -- we use this to calculate where the read data lives
1932  if(response['data_length'] == 0) then
1933    response['data'] = 0
1934  else
1935    local data_start = #header + 1 + #parameters + 2
1936    if(data_offset < data_start) then
1937      return false, "SMB: Start of data isn't in data section"
1938    end
1939
1940    -- Figure out the offset into the data section
1941    data_offset = data_offset - data_start
1942
1943    -- Make sure we don't run off the edge of the packet
1944    if(data_offset + response['data_length'] > #data) then
1945      return false, "SMB: Data returned runs off the end of the packet"
1946    end
1947
1948    -- Pull the data string out of the data
1949    response['data'] = string.sub(data, data_offset + 1, data_offset + response['data_length'])
1950  end
1951
1952  return true, response
1953end
1954
1955--- This sends a SMB request to write to a file (or a pipe).
1956--
1957--@param smb        The SMB object associated with the connection
1958--@param write_data The data to write
1959--@param offset     The offset to write it to (ignored for pipes)
1960--@param overrides  The overrides table
1961--@return (status, result) If status is false, result is an error message. Otherwise, result is a table
1962--        containing a lot of different elements, the most important one being 'fid', the handle to the opened file.
1963function write_file(smb, write_data, offset, overrides)
1964  overrides = overrides or {}
1965  local header, parameters, data
1966  local andx_command, andx_reserved, andx_offset
1967  local response = {}
1968  local status
1969
1970  header = smb_encode_header(smb, command_codes['SMB_COM_WRITE_ANDX'], overrides)
1971  parameters = string.pack("<BBI2 I2 I4 I4 I2 I2 I2 I2 I2 I4",
1972    0xFF,   -- ANDX no further commands
1973    0x00,   -- ANDX reserved
1974    0x0000, -- ANDX offset
1975    smb['fid'], -- FID
1976    offset,     -- Offset
1977    0xFFFFFFFF, -- Reserved
1978    0x0008,     -- Write mode (Message start, don't write raw, don't return remaining, don't write through
1979    #write_data,-- Remaining
1980    0x0000,     -- Data length high
1981    #write_data,-- Data length low -- TODO: set this properly (to the 2-byte value)
1982    0x003F,     -- Data offset
1983    0x00000000  -- Data offset high
1984    )
1985
1986  data = write_data
1987
1988  -- Send the create file
1989  stdnse.debug2("SMB: Sending SMB_COM_WRITE_ANDX")
1990  local result, err = smb_send(smb, header, parameters, data, overrides)
1991  if(result == false) then
1992    return false, err
1993  end
1994
1995
1996  -- Read the result
1997  status, header, parameters, data = smb_read(smb)
1998  if(status ~= true) then
1999    return false, header
2000  end
2001
2002  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
2003  if #header < string.packsize(header_format) then
2004    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
2005  end
2006
2007  -- Check if we were allowed in
2008  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
2009
2010  if(status ~= 0) then
2011    return false, get_status_name(status)
2012  end
2013
2014  -- Parse the parameters
2015  local parameters_format = "<BBI2 I2 I2 I2 I2"
2016  if #parameters < string.packsize(parameters_format) then
2017    return false, "SMB: ERROR: Server returned less data than needed"
2018  end
2019  local count_reserved, count_high, remaining, count_low
2020  andx_command, andx_reserved, andx_offset, count_low, remaining, count_high, count_reserved, pos = string.unpack(parameters_format, parameters)
2021
2022  response['count_low']  = count_low
2023  response['remaining']  = remaining
2024  response['count_high'] = count_high
2025  response['reserved']   = count_reserved
2026
2027  return true, response
2028end
2029
2030--- This sends a SMB request to close a file (or a pipe).
2031--
2032--@param smb        The SMB object associated with the connection
2033--@param overrides  The overrides table
2034--@return (status, result) If status is false, result is an error message. Otherwise, result is undefined.
2035function close_file(smb, overrides)
2036  overrides = overrides or {}
2037  local header, parameters, data
2038  local pos
2039  local status
2040  local andx_command, andx_reserved, andx_offset
2041  local response = {}
2042
2043  header = smb_encode_header(smb, command_codes['SMB_COM_CLOSE'], overrides)
2044  parameters = string.pack("<I2 I4",
2045    smb['fid'], -- FID
2046    0xFFFFFFFF  -- Last write (unspecified)
2047    )
2048
2049  data = ""
2050
2051  -- Send the close file
2052  stdnse.debug2("SMB: Sending SMB_CLOSE")
2053  local result, err = smb_send(smb, header, parameters, data, overrides)
2054  if(result == false) then
2055    return false, err
2056  end
2057
2058  -- Read the result
2059  status, header, parameters, data = smb_read(smb)
2060  if(status ~= true) then
2061    return false, header
2062  end
2063
2064  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
2065  if #header < string.packsize(header_format) then
2066    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [27]"
2067  end
2068
2069  -- Check if the close was successful
2070  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
2071
2072  if(status ~= 0) then
2073    return false, get_status_name(status)
2074  end
2075
2076  -- Close response has no parameters or data
2077  return true, response
2078end
2079
2080--- This sends a SMB request to delete a file (or a pipe).
2081--
2082--@param smb    The SMB object associated with the connection
2083--@param path   The path of the file to delete
2084--@param overrides The overrides table
2085--@return (status, result) If status is false, result is an error message. Otherwise, result is undefined.
2086function delete_file(smb, path, overrides)
2087  overrides = overrides or {}
2088  local header, parameters, data
2089  local andx_command, andx_reserved, andx_offset
2090  local status
2091
2092  header = smb_encode_header(smb, command_codes['SMB_COM_DELETE'], overrides)
2093  parameters = string.pack("<I2",
2094    0x0027 -- Search attributes (0x27 = include read only, hidden, system, and archive)
2095    )
2096
2097  data = string.pack("<Bz",
2098    0x04, -- Ascii formatted filename
2099    path)
2100
2101  -- Send the close file
2102  stdnse.debug2("SMB: Sending SMB_CLOSE")
2103  local result, err = smb_send(smb, header, parameters, data, overrides)
2104  if(result == false) then
2105    return false, err
2106  end
2107
2108  -- Read the result
2109  status, header, parameters, data = smb_read(smb)
2110  if(status ~= true) then
2111    return false, header
2112  end
2113
2114  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
2115  if #header < string.packsize(header_format) then
2116    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
2117  end
2118
2119  -- Check if the close was successful
2120  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
2121
2122  if(status ~= 0) then
2123    return false, get_status_name(status)
2124  end
2125
2126  -- Close response has no parameters or data
2127  return true
2128end
2129
2130---
2131-- Implements SMB_COM_TRANSACTION2 to support the find_files function
2132-- This function has not been extensively tested
2133--
2134--@param smb           SMB object associated with the connection
2135--@param sub_command   code of a SMB_COM_TRANSACTION2 sub command
2136--@param trans2_param  Parameter data to pass to the function
2137--@param trans2_data   Data to send with the packet
2138--@param overrides     The overrides table
2139--@return status       Boolean outcome of the request
2140--@return error        error message if the status is false
2141local function send_transaction2(smb, sub_command, trans2_param, trans2_data, overrides)
2142  overrides = overrides or {}
2143  trans2_param = trans2_param or ""
2144  trans2_data = trans2_data or ""
2145
2146  local header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION2'], overrides)
2147  local pad1 = "\0\0\0" -- Name, Pad1
2148  local pad2 = ("\0"):rep((4 - #trans2_param % 4) % 4)
2149
2150  local trans2_param_len = #trans2_param
2151    -- 68 = 32  SMB header
2152    --    + 31  SMB parameters
2153    --    +  2  SMB data ByteCount field
2154    --    +  3  #pad1
2155  local trans2_param_pos = 68
2156  local trans2_data_len = #trans2_data
2157  local trans2_data_pos = trans2_param_pos + trans2_param_len + #pad2
2158  if trans2_data_len == 0 then
2159    pad2 = ""
2160    trans2_data_pos = 0
2161  end
2162
2163  -- SMB parameters are 31 bytes long, incl. initial WordCount field
2164  -- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/f7d148cd-e3d5-49ae-8b37-9633822bfeac
2165  local parameters = string.pack("<I2 I2 I2 I2 BB I2 I4 I2 I2 I2 I2 I2 BB I2 ",
2166            trans2_param_len,    -- Total parameter count
2167            trans2_data_len,     -- Total data count
2168            0x000a,              -- Max parameter count
2169            0xff80,              -- Max data count
2170            0x00,                -- Max setup count
2171            0x00,                -- Reserved
2172            0x0000,              -- Flags (2-way transaction, don't disconnect TIDs)
2173            5000,                -- Timeout (ms)
2174            0x0000,              -- Reserved
2175            trans2_param_len,    -- Parameter count
2176            trans2_param_pos,    -- Parameter offset
2177            trans2_data_len,     -- Data count
2178            trans2_data_pos,     -- Data offset
2179            0x01,                -- Setup count
2180            0x00,                -- Reserved
2181            sub_command          -- Sub command
2182            )
2183
2184  local data = pad1 .. trans2_param .. pad2 .. trans2_data
2185
2186  -- Send the transaction request
2187  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION2")
2188  return smb_send(smb, header, parameters, data, overrides)
2189end
2190
2191local function receive_transaction2(smb)
2192
2193  -- Read the result
2194  local status, header, parameters, data = smb_read(smb)
2195  if(status ~= true) then
2196    return false, header
2197  end
2198
2199  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
2200  if #header < string.packsize(header_format) then
2201    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
2202  end
2203
2204  -- Check if it worked
2205  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
2206
2207  if(status ~= 0) then
2208    if(status_names[status] == nil) then
2209      return false, string.format("Unknown SMB error: 0x%08x\n", status)
2210    else
2211      return false, status_names[status]
2212    end
2213  end
2214
2215  -- Parse the parameters
2216  local parameters_format = "<I2 I2 I2 I2 I2 I2 I2 I2 I2 BB"
2217  if #parameters < string.packsize(parameters_format) then
2218    return false, "SMB: ERROR: Server returned less data than needed"
2219  end
2220  local total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2, pos = string.unpack(parameters_format, parameters)
2221
2222  -- Convert the parameter/data offsets into something more useful (the offset into the data section)
2223  -- - 0x20 for the header, - 0x01 for the length.
2224  parameter_offset = parameter_offset - 0x20 - 0x01 - #parameters - 0x02;
2225  -- - 0x20 for the header, - 0x01 for parameter length, the parameter length, and - 0x02 for the data length.
2226  data_offset = data_offset - 0x20 - 0x01 - #parameters - 0x02;
2227
2228  -- I'm not sure I entirely understand why the '+1' is here, but I think it has to do with the string starting at '1' and not '0'.
2229  local function_parameters = string.sub(data, parameter_offset + 1, parameter_offset + parameter_count)
2230  local function_data       = string.sub(data, data_offset      + 1, data_offset      + data_count)
2231
2232  local response = {}
2233  response['parameters'] = function_parameters
2234  response['data']       = function_data
2235
2236  return true, response
2237end
2238
2239
2240
2241---This is the core of making MSRPC calls. It sends out a MSRPC packet with the
2242-- given parameters and data.
2243--
2244-- Don't confuse these parameters and data with SMB's concepts of parameters
2245-- and data -- they are completely different. In fact, these parameters and
2246-- data are both sent in the SMB packet's 'data' section.
2247--
2248-- It is probably best to think of this as another protocol layer. This
2249-- function will wrap SMB stuff around a MSRPC call, make the call, then unwrap
2250-- the SMB stuff from it before returning.
2251--
2252--@param smb The SMB object associated with the connection
2253--@param function_parameters The parameter data to pass to the function. This
2254--                           is untested, since none of the transactions I've
2255--                           done have required parameters.
2256--@param function_data The data to send with the packet. This is basically the
2257--                     next protocol layer
2258--@param pipe [optional] The pipe to transact on. Default: "\PIPE\".
2259--@param no_setup [optional] If set, the 'setup' is set to 0 and some
2260--                parameters are left off. This occurs while using the LANMAN
2261--                Remote API. Default: false.
2262--@param overrides The overrides table
2263--@return (status, result) If status is false, result is an error message.
2264--        Otherwise, result is a table containing 'parameters' and 'data',
2265--        representing the parameters and data returned by the server.
2266function send_transaction_named_pipe(smb, function_parameters, function_data, pipe, no_setup, overrides)
2267  overrides = overrides or {}
2268  local header, parameters, data
2269  local parameter_offset = 0
2270  local parameter_size   = 0
2271  local data_offset      = 0
2272  local data_size        = 0
2273  local total_word_count, total_data_count, reserved1, parameter_count, parameter_displacement, data_count, data_displacement, setup_count, reserved2
2274  local response = {}
2275  local status
2276
2277  if(pipe == nil) then
2278    pipe = "\\PIPE\\"
2279  end
2280
2281  -- Header is 0x20 bytes long (not counting NetBIOS header).
2282  header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION'], overrides) -- 0x25 = SMB_COM_TRANSACTION
2283
2284  -- 0x20 for SMB header, 0x01 for parameters header, 0x20 for parameters length, 0x02 for data header, 0x07 for "\PIPE\"
2285  if(function_parameters) then
2286    parameter_offset = 0x20 + 0x01 + 0x20 + 0x02 + (#pipe + 1)
2287    parameter_size = #function_parameters
2288  end
2289
2290  if(function_data) then
2291    data_offset       = 0x20 + 0x01 + 0x20 + 0x02 + (#pipe + 1) + parameter_size
2292    data_size         = #function_data
2293  end
2294
2295  local setup
2296  if(no_setup) then
2297    setup = string.pack("<BB",
2298      0x00,                            -- Number of 'setup' words (none)
2299      0x00                             -- Reserved.
2300      )
2301  else
2302    setup = string.pack("<BBI2 I2 ",
2303      0x02,                            -- Number of 'setup' words
2304      0x00,                            -- Reserved.
2305      0x0026,                          -- Function to call.
2306      smb['fid']                       -- Handle to open file
2307      )
2308  end
2309
2310  -- Parameters are 0x20 bytes long.
2311  parameters = string.pack("<I2 I2 I2 I2 BBI2 I4 I2 I2 I2 I2 I2",
2312    parameter_size,                  -- Total parameter count.
2313    data_size,                       -- Total data count.
2314    0x0008,                          -- Max parameter count.
2315    0x3984,                          -- Max data count.
2316    0x00,                            -- Max setup count.
2317    0x00,                            -- Reserved.
2318    0x0000,                          -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
2319    0x00001388,                      -- Timeout (0x00000000 = return immediately).
2320    0x0000,                          -- Reserved.
2321    parameter_size,                  -- Parameter bytes.
2322    parameter_offset,                -- Parameter offset.
2323    data_size,                       -- Data bytes.
2324    data_offset                      -- Data offset.
2325    ) .. setup
2326
2327  data = string.pack("<zI4", pipe, 0) -- Padding
2328  .. (function_parameters or '')
2329  .. (function_data or '')
2330
2331  -- Send the transaction request
2332  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION")
2333  local result, err = smb_send(smb, header, parameters, data, overrides)
2334  if(result == false) then
2335    return false, err
2336  end
2337
2338  -- Read the result
2339  status, header, parameters, data = smb_read(smb)
2340  if(status ~= true) then
2341    return false, header
2342  end
2343
2344  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
2345  if #header < string.packsize(header_format) then
2346    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
2347  end
2348
2349  -- Check if it worked
2350  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
2351
2352  if(status ~= 0) then
2353    if(status_names[status] == nil) then
2354      return false, string.format("Unknown SMB error: 0x%08x\n", status)
2355    else
2356      return false, status_names[status]
2357    end
2358  end
2359
2360  -- Parse the parameters
2361  local parameters_format = "<I2 I2 I2 I2 I2 I2 I2 I2 I2 BB"
2362  if #parameters < string.packsize(parameters_format) then
2363    return false, "SMB: ERROR: Server returned less data than needed"
2364  end
2365  total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2, pos = string.unpack(parameters_format, parameters)
2366
2367  -- Convert the parameter/data offsets into something more useful (the offset into the data section)
2368  -- - 0x20 for the header, - 0x01 for the length.
2369  parameter_offset = parameter_offset - 0x20 - 0x01 - #parameters - 0x02;
2370  -- - 0x20 for the header, - 0x01 for parameter length, the parameter length, and - 0x02 for the data length.
2371  data_offset = data_offset - 0x20 - 0x01 - #parameters - 0x02;
2372
2373  -- I'm not sure I entirely understand why the '+1' is here, but I think it has to do with the string starting at '1' and not '0'.
2374  function_parameters = string.sub(data, parameter_offset + 1, parameter_offset + parameter_count)
2375  function_data       = string.sub(data, data_offset      + 1, data_offset      + data_count)
2376
2377  response['parameters'] = function_parameters
2378  response['data']       = function_data
2379
2380  return true, response
2381end
2382
2383function send_transaction_waitnamedpipe(smb, priority, pipe, overrides)
2384  overrides = overrides or {}
2385  local header, parameters, data
2386  local parameter_offset, data_offset
2387  local total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2
2388  local response = {}
2389  local padding = ""
2390  local status
2391
2392  -- Header is 0x20 bytes long (not counting NetBIOS header).
2393  header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION'], overrides) -- 0x25 = SMB_COM_TRANSACTION
2394
2395  -- Parameters are 0x20 bytes long.
2396  parameters = string.pack("<I2 I2 I2 I2 BBI2 I4 I2 I2 I2 I2 I2 BBI2 I2 ",
2397    0,                               -- Total parameter count.
2398    0,                               -- Total data count.
2399    0x000,                           -- Max parameter count.
2400    0x400,                           -- Max data count.
2401    0x00,                            -- Max setup count.
2402    0x00,                            -- Reserved.
2403    0x0000,                          -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
2404    30,                              -- Timeout (0x00000000 = return immediately).
2405    0x0000,                          -- Reserved.
2406    0,                               -- Parameter bytes.
2407    0,                               -- Parameter offset.
2408    0,                               -- Data bytes.
2409    0,                               -- Data offset.
2410    0x02,                            -- Number of 'setup' words (only ever seen '2').
2411    0x00,                            -- Reserved.
2412    0x0053,                          -- Function to call.
2413    priority                         -- Handle to open file
2414    )
2415
2416  data = string.pack("z", pipe) .. string.rep('\0', (4 - ((#pipe+1) % 4)) % 4)
2417
2418  -- Send the transaction request
2419  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION (WaitNamedPipe)")
2420  local result, err = smb_send(smb, header, parameters, data, overrides)
2421  if(result == false) then
2422    return false, err
2423  end
2424
2425  -- Read the result
2426  status, header, parameters, data = smb_read(smb)
2427  if(status ~= true) then
2428    return false, header
2429  end
2430
2431  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
2432  if #header < string.packsize(header_format) then
2433    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
2434  end
2435
2436  -- Parse out the header
2437  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
2438
2439  if(status ~= 0) then
2440    if(status_names[status] == nil) then
2441      return false, string.format("Unknown SMB error: 0x%08x\n", status)
2442    else
2443      return false, status_names[status]
2444    end
2445  end
2446
2447  -- Parse the parameters
2448  local parameters_format = "<I2 I2 I2 I2 I2 I2 I2 I2 I2 BB"
2449  if #parameters < string.packsize(parameters_format) then
2450    return false, "SMB: ERROR: Server returned less data than needed"
2451  end
2452  total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2, pos = string.unpack(parameters_format, parameters)
2453
2454  return true, response
2455end
2456
2457---Upload a file from the local machine to the remote machine, on the given share.
2458--
2459--@param host       The host object
2460--@param localfile  The file on the local machine, relative to the nmap path
2461--@param share      The share to upload it to (eg, C$).
2462--@param remotefile The remote file on the machine. It is relative to the share's root.
2463--@param overrides  A table of override values that's passed to the smb functions.
2464--@param encoded    Set to 'true' if the file is encoded (xor'ed with 0xFF), It will be decoded before upload. Default: false
2465--@return (status, err) If status is false, err is an error message. Otherwise, err is undefined.
2466function file_upload(host, localfile, share, remotefile, overrides, encoded)
2467  local status, err, smbstate
2468  local chunk = 1024
2469
2470  -- Attempt to open a handle to the file without adding a path to it
2471  local handle = io.open(localfile, "r")
2472
2473  -- If the open failed, try to search for the file
2474  if(not(handle)) then
2475    stdnse.debug1("Couldn't open %s directly, searching Nmap's paths...", localfile)
2476    local filename = nmap.fetchfile(localfile)
2477
2478    -- Check if it was found
2479    if(filename == nil) then
2480      return false, string.format("Couldn't find the file to upload (%s)", localfile)
2481    end
2482    handle = io.open(filename, "r")
2483  end
2484
2485  -- Create the SMB session
2486  status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides)
2487  if(status == false) then
2488    return false, smbstate
2489  end
2490
2491
2492  local i = 0
2493  local data = handle:read(chunk)
2494  local new_data = {}
2495  while(data ~= nil and #data > 0) do
2496
2497    if(encoded) then
2498      for j = 1, #data, 1 do
2499        new_data[j] = string.char(0xFF ~ string.byte(data, j))
2500      end
2501      data = table.concat(new_data, "", 1, #data)
2502    end
2503
2504    status, err = write_file(smbstate, data, i)
2505    if(status == false) then
2506      stop(smbstate)
2507      return false, err
2508    end
2509
2510    data = handle:read(chunk)
2511    i = i + chunk
2512  end
2513
2514  handle:close()
2515  status, err = close_file(smbstate)
2516  if(status == false) then
2517    stop(smbstate)
2518    return false, err
2519  end
2520
2521  -- Stop the session
2522  stop(smbstate)
2523
2524  return true
2525end
2526
2527---Write given data to the remote machine on the given share. This is similar to <code>file_upload</code>, except the
2528-- data is given as a string, not a file.
2529--
2530--@param host          The host object
2531--@param data          The string containing the data to be written
2532--@param share         The share to upload it to (eg, C$).
2533--@param remotefile    The remote file on the machine. It is relative to the share's root.
2534--@param use_anonymous [optional] If set to 'true', test is done by the anonymous user rather than the current user.
2535--@return (status, err) If status is false, err is an error message. Otherwise, err is undefined.
2536function file_write(host, data, share, remotefile, use_anonymous)
2537  local status, err, smbstate
2538  local chunk = 1024
2539  local overrides = nil
2540
2541  -- If anonymous is being used, create some overrides
2542  if(use_anonymous) then
2543    overrides = get_overrides_anonymous()
2544  end
2545
2546  -- Create the SMB session
2547  status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides)
2548
2549  if(status == false) then
2550    return false, smbstate
2551  end
2552
2553  local i = 1
2554  while(i <= #data) do
2555    local chunkdata = string.sub(data, i, i + chunk - 1)
2556    status, err = write_file(smbstate, chunkdata, i - 1)
2557    if(status == false) then
2558      stop(smbstate)
2559      return false, err
2560    end
2561
2562    i = i + chunk
2563  end
2564
2565  status, err = close_file(smbstate)
2566  if(status == false) then
2567    stop(smbstate)
2568    return false, err
2569  end
2570
2571  -- Stop the session
2572  stop(smbstate)
2573
2574  return true
2575end
2576
2577---Write given data to the remote machine on the given share. This is similar to <code>file_upload</code>, except the
2578-- data is given as a string, not a file.
2579--
2580--@param host          The host object
2581--@param share         The share to read it from (eg, C$).
2582--@param remotefile    The remote file on the machine. It is relative to the share's root.
2583--@param use_anonymous [optional] If set to 'true', test is done by the anonymous user rather than the current user.
2584--@param overrides     [optional] Override various fields in the SMB packets.
2585--@return (status, err) If status is false, err is an error message. Otherwise, err is undefined.
2586function file_read(host, share, remotefile, use_anonymous, overrides)
2587  local status, err, smbstate
2588  local result
2589  local chunk = 1024
2590  local read = ""
2591
2592  -- Make sure we got overrides
2593  overrides = overrides or {}
2594
2595  -- If anonymous is being used, create some overrides
2596  if(use_anonymous) then
2597    overrides = get_overrides_anonymous(overrides)
2598  end
2599
2600  -- Create the SMB session
2601  status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides)
2602
2603  if(status == false) then
2604    return false, smbstate
2605  end
2606
2607  local i = 1
2608  while true do
2609    status, result = read_file(smbstate, i - 1, chunk)
2610    if(status == false) then
2611      stop(smbstate)
2612      return false, result
2613    end
2614
2615    if(result['data_length'] == 0) then
2616      break
2617    end
2618
2619    read = read .. result['data']
2620    i = i + chunk
2621  end
2622
2623  status, err = close_file(smbstate)
2624  if(status == false) then
2625    stop(smbstate)
2626    return false, err
2627  end
2628
2629  -- Stop the session
2630  stop(smbstate)
2631  return true, read
2632end
2633
2634---Check how many files, in a given list, exist on the given share.
2635--
2636--@param host          The host object
2637--@param share         The share to read it from (eg, C$).
2638--@param files         A list of files to look for; it is relative to the share's root.
2639--@param overrides     [optional] Override various fields in the SMB packets.
2640--@return status: A true/false value indicating success
2641--@return count:  The number of files that existed, or an error message if status is 'false'
2642--@return files:  A list of the files that existed.
2643function files_exist(host, share, files, overrides)
2644  local status, smbstate, result, err
2645
2646  -- Make sure we got overrides
2647  overrides = overrides or {}
2648
2649  -- We don't wan to be creating the files
2650  overrides['file_create_disposition'] = 1
2651
2652  -- Create the SMB session
2653  status, smbstate = start_ex(host, true, true, share, nil, nil, overrides)
2654
2655  if(status == false) then
2656    return false, smbstate
2657  end
2658
2659  local exist = 0
2660  local list  = {}
2661
2662  for _, file in ipairs(files) do
2663    -- Try and open the file
2664    status, result = create_file(smbstate, file, overrides)
2665
2666    -- If there was an error other than 'file already exists', return an error
2667    if(not(status) and result ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then
2668      return false, result
2669    end
2670
2671    -- If the file existed, count it and close it
2672    if(status) then
2673      exist = exist + 1
2674      table.insert(list, file)
2675      status, err = close_file(smbstate)
2676      if(status == false) then
2677        stop(smbstate)
2678        return false, err
2679      end
2680    end
2681  end
2682
2683  -- Stop the session
2684  stop(smbstate)
2685  return true, exist, list
2686end
2687
2688---Delete a file from the remote machine
2689--
2690--@param host       The host object
2691--@param share      The share to upload it to (eg, C$).
2692--@param remotefile The remote file on the machine. It is relative to the share's root. It can be a string, or an array.
2693--@return (status, err) If status is false, err is an error message. Otherwise, err is undefined.
2694function file_delete(host, share, remotefile)
2695  local status, smbstate, err
2696
2697  -- Create the SMB session
2698  status, smbstate = start_ex(host, true, true, share)
2699  if(status == false) then
2700    return false, smbstate
2701  end
2702
2703  -- Make sure the remotefile is always a table, to save on duplicate code
2704  if(type(remotefile) ~= "table") then
2705    remotefile = {remotefile}
2706  end
2707
2708
2709  for _, file in ipairs(remotefile) do
2710    status, err = delete_file(smbstate, file)
2711    if(status == false) then
2712      stdnse.debug1("SMB: Couldn't delete %s\\%s: %s", share, file, err)
2713      if(err ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then
2714        stop(smbstate)
2715        return false, err
2716      end
2717    end
2718  end
2719
2720  -- Stop the session
2721  stop(smbstate)
2722
2723  return true
2724end
2725
2726-- Sends TRANS2_FIND_FIRST2 / TRANS2_FIND_NEXT2 request, takes care of
2727-- short/fragmented responses, and returns a list of file entries
2728--
2729-- @param smbstate the SMB object associated with the connection
2730-- @param srch_id of search to resume (for TRANS2_FIND_NEXT2) or nil
2731-- @param trans2_params string representing Trans2_Parameters
2732-- @return status of the request
2733-- @return srch_id of search to resume later, or nil if the search completed
2734--                 or the error message if status is false
2735-- @return list of file entries
2736local function send_and_receive_find_request(smbstate, srch_id, trans2_params)
2737  local TRANS2_FIND_FIRST2 = 1
2738  local TRANS2_FIND_NEXT2  = 2
2739  local sub_command = srch_id and TRANS2_FIND_NEXT2 or TRANS2_FIND_FIRST2
2740  local status = send_transaction2(smbstate, sub_command, trans2_params, "")
2741  if not status then
2742    return false, "Failed to send data to server: send_transaction2"
2743  end
2744
2745  local resp
2746  status, resp = receive_transaction2(smbstate)
2747  if not status or #resp.parameters < 2 then
2748    return false, "Failed to receive data from server: receive_transaction2"
2749  end
2750
2751  local param_pos = 1
2752  if sub_command == TRANS2_FIND_FIRST2 then
2753    srch_id, param_pos = string.unpack("<I2", resp.parameters, param_pos)
2754  end
2755
2756  -- parse Trans2_Parameters
2757  -- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/4e65d94e-09af-4511-a77a-b73adf1c52d6
2758  local param_fmt = "<I2 I2 xx I2"
2759  if #resp.parameters < param_pos - 1 + param_fmt:packsize() then
2760    return false, "Truncated response from server: receive_transaction2"
2761  end
2762  local srch_cnt, srch_end, last_name_pos = param_fmt:unpack(resp.parameters, param_pos)
2763
2764  -- format of SMB_FIND_FILE_BOTH_DIRECTORY_INFO, without trailing FileName
2765  -- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/2aa849f4-1bc0-42bf-9c8f-d09f11fccc4c
2766  local entry_fmt = "<I4 xxxx I8 I8 I8 I8 I8 I8 I4 I4 xxxx B x c24"
2767  local entry_len = entry_fmt:packsize()
2768
2769  -- check if we need more packets to reassemble this transaction
2770  while #resp.data < last_name_pos + entry_len do
2771    local status, tmp = receive_transaction2(smbstate)
2772    if not status then
2773      return false, "Truncated response from receive_transaction2"
2774    end
2775    resp.data = resp.data .. tmp.data
2776  end
2777
2778  -- parse response, based on SMB_FIND_FILE_BOTH_DIRECTORY_INFO
2779  local entries = {}
2780  local data_pos = 1
2781  while srch_cnt > 0 do
2782    if #resp.data - data_pos + 1 < entry_len then
2783      return false, "Truncated response from receive_transaction2"
2784    end
2785    local entry = {}
2786    local next_pos, fn_pos, fn_len, sfn_len
2787    next_pos, entry.created, entry.accessed, entry.write, entry.change,
2788      entry.eof, entry.alloc_size, entry.attrs, fn_len, sfn_len,
2789      entry.s_fname, fn_pos = entry_fmt:unpack(resp.data, data_pos)
2790
2791    local time = entry.created
2792    time = (time // 10000000) - 11644473600
2793    entry.created = datetime.format_timestamp(time)
2794
2795    if sfn_len > 0 then
2796      entry.s_fname = entry.s_fname:sub(1, sfn_len)
2797    else
2798      entry.s_fname = nil
2799    end
2800
2801    if #resp.data - fn_pos + 1 < fn_len then
2802      return false, "Truncated response from receive_transaction2"
2803    end
2804    entry.fname = string.unpack("z", resp.data, fn_pos)
2805    table.insert(entries, entry)
2806    data_pos = data_pos + next_pos
2807    srch_cnt = srch_cnt - 1
2808  end
2809  return true, (srch_end == 0 and srch_id or nil), entries
2810end
2811
2812---
2813-- List files based on a pattern within a given share and directory
2814--
2815-- @param smbstate the SMB object associated with the connection
2816-- @param fname filename to search for, relative to share path
2817-- @param options table containing none or more of the following
2818--        <code>maxfiles</code> how many files to request in a single Trans2 op
2819--        <code>srch_attrs</code> table containing one or more of the following boolean attributes:
2820--              <code>ro</code> - find read only files
2821--              <code>hidden</code> - find hidden files
2822--              <code>system</code> - find system files
2823--              <code>volid</code> - include volume ids in result
2824--              <code>dir</code> - find directories
2825--              <code>archive</code> - find archived files
2826-- @return iterator function retrieving the next result
2827function find_files (smbstate, fname, options)
2828  options = options or {}
2829
2830  -- convert options.srch_attrs to a bitmap
2831  local xlat_srch_attrs = {ro      = "SMB_FILE_ATTRIBUTE_READONLY",
2832                           hidden  = "SMB_FILE_ATTRIBUTE_HIDDEN",
2833                           system  = "SMB_FILE_ATTRIBUTE_SYSTEM",
2834                           volid   = "SMB_FILE_ATTRIBUTE_VOLUME",
2835                           dir     = "SMB_FILE_ATTRIBUTE_DIRECTORY",
2836                           archive = "SMB_FILE_ATTRIBUTE_ARCHIVE"}
2837  local srch_attrs_mask = 0
2838  local srch_attrs = options.srch_attrs or {ro=true, hidden=false, system=true, dir=true}
2839  for k, v in pairs(srch_attrs) do
2840    if v then
2841      srch_attrs_mask = srch_attrs_mask | file_attributes[xlat_srch_attrs[k]]
2842    end
2843  end
2844
2845  fname = fname or '\\*'
2846  if fname:sub(1,1) ~= '\\' then
2847    fname = '\\' .. fname
2848  end
2849
2850  local srch_flags = 0x0002 | 0x0004 -- SMB_FIND_CLOSE_AT_EOS, SMB_FIND_RETURN_RESUME_KEYS
2851  local srch_info_lvl = 0x0104       -- SMB_FIND_FILE_BOTH_DIRECTORY_INFO
2852  local max_srch_cnt = tonumber(options.maxfiles)
2853  if max_srch_cnt and max_srch_cnt > 0 then
2854    max_srch_cnt = math.floor(4 + math.min(1020, max_srch_cnt))
2855  else
2856    max_srch_cnt = 1024
2857  end
2858
2859  -- state variables for next_entry() iterator
2860  local first_run = true
2861  local srch_id = nil
2862  local last_fname = nil
2863  local entries = {}
2864  local entry_idx = 1
2865
2866  local function next_entry()
2867    if entry_idx > #entries then  -- get more file entries from the target
2868      local trans2_params
2869      if first_run then -- TRANS2_FIND_FIRST2
2870        first_run = false
2871        -- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/b2b2a730-9499-4f05-884e-d5bb7b9caf90
2872        trans2_params = string.pack("<I2 I2 I2 I2 I4 z",
2873                                  srch_attrs_mask, -- what types of files to return
2874                                  max_srch_cnt,    -- maximum number of returned entries
2875                                  srch_flags,      -- Flags
2876                                  srch_info_lvl,   -- level of returned file details
2877                                  0,               -- SearchStorageType
2878                                  fname)           -- file name to search for
2879      -- FIXME filename ASCII vs UNICODE
2880      else -- TRANS2_FIND_NEXT2
2881        if not srch_id then  -- the search is over
2882          return
2883        end
2884        -- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/80dc980e-fe03-455c-ada6-7c5dd6c551ba
2885        trans2_params = string.pack("<I2 I2 I2 I4 I2 z",
2886                                  srch_id,         -- which search to resume
2887                                  max_srch_cnt,    -- maximum number of returned entries
2888                                  srch_info_lvl,   -- level of returned file details
2889                                  0,               -- ResumeKey
2890                                  srch_flags,      -- Flags
2891                                  last_fname)      -- last file name previously returned
2892        -- FIXME wtf is ResumeKey?
2893      end
2894      local status
2895      status, srch_id, entries = send_and_receive_find_request(smbstate, srch_id, trans2_params)
2896      if not status then
2897        stdnse.debug1("Routine find_files failed with error: %s", srch_id)
2898        srch_id = nil
2899        entries = {}
2900      end
2901      entry_idx = 1
2902      if #entries == 0 then
2903        return
2904      end
2905    end
2906    local entry = entries[entry_idx]
2907    last_fname = entry.fname
2908    entry_idx = entry_idx + 1
2909    return entry
2910  end
2911  return next_entry
2912end
2913
2914---Determine whether or not the anonymous user has write access on the share. This is done by creating then
2915-- deleting a file.
2916--
2917--@param host     The host object
2918--@param share    The share to test
2919--@return (status, result) If status is false, result is an error message. The error message 'NT_STATUS_OBJECT_NAME_NOT_FOUND'
2920--        should be handled gracefully; it indicates that the share isn't a fileshare. Otherwise, result is a boolean value:
2921--        true if the file was successfully written, false if it was not.
2922function share_anonymous_can_write(host, share)
2923  local filename, status, err
2924
2925  -- First, choose a filename. This should be random.
2926  filename = "nmap-test-file"
2927
2928  -- Next, attempt to write to that file
2929  status, err = file_write(host, string.rep("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10), share, filename, true)
2930  if(status == false) then
2931    if(err == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
2932      return false, err
2933    end
2934
2935    if(err == "NT_STATUS_ACCESS_DENIED" or err == "NT_STATUS_INVALID_PARAMETER") then
2936      return true, false
2937    end
2938
2939    return false, "Error writing test file to disk as anonymous: " .. err
2940  end
2941
2942  -- Now the important part: delete it
2943  status, err = file_delete(host, share, filename)
2944  if(status == false) then
2945    return false, "Error deleting test file as anonymous: " .. err
2946  end
2947
2948  return true, true
2949end
2950
2951
2952---Determine whether or not the current user has read or read/write access on the share. This is done by creating then
2953-- deleting a file.
2954--
2955--@param host     The host object
2956--@param share    The share to test
2957--@return (status, result) If status is false, result is an error message. The error message 'NT_STATUS_OBJECT_NAME_NOT_FOUND'
2958--        should be handled gracefully; it indicates that the share isn't a fileshare. Otherwise, result is a boolean value:
2959--        true if the file was successfully written, false if it was not.
2960function share_user_can_write(host, share)
2961
2962  local filename, status, err
2963
2964  -- First, choose a filename. This should be random.
2965  filename = "nmap-test-file"
2966
2967  -- Next, attempt to write to that file
2968  status, err = file_write(host, string.rep("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10), share, filename)
2969  if(status == false) then
2970    if(err == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
2971      return false, err
2972    end
2973
2974    if(err == "NT_STATUS_ACCESS_DENIED" or err == "NT_STATUS_INVALID_PARAMETER") then
2975      return true, false
2976    end
2977
2978    return false, "Error writing test file to disk as user: " .. err
2979  end
2980
2981  -- Now the important part: delete it
2982  status, err = file_delete(host, share, filename)
2983  if(status == false) then
2984    return false, "Error deleting test file as user: " .. err
2985  end
2986
2987  return true, true
2988end
2989
2990---Check whether or not a share is accessible by the anonymous user. Assumes that <code>share_host_returns_proper_error</code>
2991-- has been called and returns <code>true</code>.
2992--
2993--@param host     The host object
2994--@param share    The share to test
2995--@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value:
2996--        true if anonymous access is permitted, false otherwise.
2997function share_anonymous_can_read(host, share)
2998  local status, smbstate, err
2999  local overrides = get_overrides_anonymous()
3000
3001  -- Begin the SMB session
3002  status, smbstate = start(host)
3003  if(status == false) then
3004    return false, smbstate
3005  end
3006
3007  -- Negotiate the protocol
3008  status, err = negotiate_protocol(smbstate, overrides)
3009  if(status == false) then
3010    stop(smbstate)
3011    return false, err
3012  end
3013
3014  -- Start up a null session
3015  status, err = start_session(smbstate, overrides)
3016
3017  if(status == false) then
3018    stop(smbstate)
3019    return false, err
3020  end
3021
3022  -- Attempt a connection to the share
3023  status, err = tree_connect(smbstate, share, overrides)
3024  if(status == false) then
3025
3026    -- Stop the session
3027    stop(smbstate)
3028
3029    -- ACCESS_DENIED is the expected error: it tells us that the connection failed
3030    if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
3031      return true, false
3032    else
3033      return false, err
3034    end
3035  end
3036
3037
3038
3039  stop(smbstate)
3040  return true, true
3041end
3042
3043---Check whether or not a share is accessible by the current user. Assumes that <code>share_host_returns_proper_error</code>
3044-- has been called and returns <code>true</code>.
3045--
3046--@param host     The host object
3047--@param share    The share to test
3048--@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value:
3049--        true if anonymous access is permitted, false otherwise.
3050function share_user_can_read(host, share)
3051  local status, smbstate, err
3052  local overrides = {}
3053
3054  -- Begin the SMB session
3055  status, smbstate = start(host)
3056  if(status == false) then
3057    return false, smbstate
3058  end
3059
3060  -- Negotiate the protocol
3061  status, err = negotiate_protocol(smbstate, overrides)
3062  if(status == false) then
3063    stop(smbstate)
3064    return false, err
3065  end
3066
3067  -- Start up a null session
3068  status, err = start_session(smbstate, overrides)
3069  if(status == false) then
3070    stop(smbstate)
3071    return false, err
3072  end
3073
3074  -- Attempt a connection to the share
3075  status, err = tree_connect(smbstate, share, overrides)
3076  if(status == false) then
3077
3078    -- Stop the session
3079    stop(smbstate)
3080
3081    -- ACCESS_DENIED is the expected error: it tells us that the connection failed
3082    if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
3083      return true, false
3084    else
3085      return false, err
3086    end
3087  end
3088
3089  stop(smbstate)
3090  return true, true
3091end
3092
3093---Determine whether or not a host will accept any share name (I've seen this on certain systems; it's
3094-- bad, because it means we cannot tell whether or not a share exists).
3095--
3096--@param host     The host object
3097--@param use_anonymous [optional] If set to 'true', test is done by the anonymous user rather than the current user.
3098--@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value:
3099--        true if the file was successfully written, false if it was not.
3100function share_host_returns_proper_error(host, use_anonymous)
3101  local status, smbstate, err
3102  local share = "nmap-share-test"
3103  local overrides
3104
3105  if ( use_anonymous ) then
3106    overrides = get_overrides_anonymous()
3107  end
3108
3109  -- Begin the SMB session
3110  status, smbstate = start(host)
3111  if(status == false) then
3112    return false, smbstate
3113  end
3114
3115  -- Negotiate the protocol
3116  status, err = negotiate_protocol(smbstate, overrides)
3117  if(status == false) then
3118    stop(smbstate)
3119    return false, err
3120  end
3121
3122  -- Start up a null session
3123  status, err = start_session(smbstate, overrides)
3124  if(status == false) then
3125    stop(smbstate)
3126    return false, err
3127  end
3128
3129  -- Connect to the share
3130  stdnse.debug1("SMB: Trying a random share to see if server responds properly: %s", share)
3131  status, err = tree_connect(smbstate, share, overrides)
3132
3133  if(status == false) then
3134    -- If the error is NT_STATUS_ACCESS_DENIED (0xc0000022), that's bad -- we don't want non-existent shares
3135    -- showing up as 'access denied'. Any other error is ok.
3136    if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
3137      stdnse.debug1("SMB: Server doesn't return proper value for non-existent shares (returns ACCESS_DENIED)")
3138      stop(smbstate)
3139      return true, false
3140    end
3141  else
3142    -- If we were actually able to connect to this share, then there's probably a serious issue
3143    stdnse.debug1("SMB: Server doesn't return proper value for non-existent shares (accepts the connection)")
3144    stop(smbstate)
3145    return true, false
3146  end
3147
3148  stop(smbstate)
3149  return true, true
3150end
3151
3152---Get all the details we can about the share. These details are stored in a table and returned.
3153--
3154--@param host   The host object.
3155--@param share An array of shares to check.
3156--@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value:
3157--        true if the file was successfully written, false if it was not.
3158function share_get_details(host, share)
3159  local msrpc = require "msrpc" -- avoid require cycle
3160  local smbstate, status, result
3161  local i
3162  local details = {}
3163
3164  --Transform name to FQPN form
3165  status, share = get_fqpn(host, share)
3166  if not status then
3167    stdnse.debug1("SMB:Couldn't obtain FQPN share name. Trying with '%s'", share)
3168  end
3169
3170  -- Save the name
3171  details['name'] = share
3172
3173  -- Check if the current user can read the share
3174  stdnse.debug1("SMB: Checking if share %s can be read by the current user", share)
3175  status, result = share_user_can_read(host, share)
3176  if(status == false) then
3177    return false, result
3178  end
3179  details['user_can_read'] = result
3180
3181  -- Check if the anonymous reader can read the share
3182  stdnse.debug1("SMB: Checking if share %s can be read by the anonymous user", share)
3183  status, result = share_anonymous_can_read(host, share)
3184  if(status == true) then
3185    details['anonymous_can_read'] = result
3186  end
3187
3188  -- Check if the current user can write to the share
3189  stdnse.debug1("SMB: Checking if share %s can be written by the current user", share)
3190  status, result = share_user_can_write(host, share)
3191  if(status == false) then
3192    if(result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
3193      details['user_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND"
3194    else
3195      return false, result
3196    end
3197  end
3198  details['user_can_write'] = result
3199
3200  -- Check if the anonymous user can write to the share
3201  stdnse.debug1("SMB: Checking if share %s can be written by the anonymous user", share)
3202  status, result = share_anonymous_can_write(host, share)
3203  if(status == false and result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
3204    details['anonymous_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND"
3205  elseif( status == true ) then
3206    details['anonymous_can_write'] = result
3207  end
3208
3209  -- Try and get full details about the share
3210  status, result = msrpc.get_share_info(host, share)
3211  if(status == false) then
3212    -- We don't stop for this error (it's pretty common since administrative privileges are required here)
3213    stdnse.debug1("SMB: Failed to get share info for %s: %s", share, result)
3214    details['details'] = result
3215  else
3216    -- Process the result a bit
3217    result = result['info']
3218    if(result['max_users'] == 0xFFFFFFFF) then
3219      result['max_users'] = "<unlimited>"
3220    end
3221    details['details'] = result
3222  end
3223
3224  return true, details
3225end
3226
3227---Retrieve a list of fileshares, along with any details that could be pulled. This is the core of smb-enum-shares.nse, but
3228-- can also be used by any script that needs to find an open share.
3229--
3230-- In the best care, the shares are determined by calling <code>msrpc.enum_shares</code>, and information is gathered by calling
3231-- <code>msrpc.get_share_info</code>. These require a certain level of access, though, so as a fallback, a pre-programmed list of
3232-- shares is used, and these are verified by attempting a connection.
3233--
3234--@param host The host object.
3235--@return (status, result, extra) If status is false, result is an error message. Otherwise, result is an array of shares with as much
3236--        detail as we could get. If extra isn't nil, it is set to extra information that should be displayed (such as a warning).
3237function share_get_list(host)
3238  local msrpc = require "msrpc" -- avoid require cycle
3239  local status, result
3240  local enum_status
3241  local extra = ""
3242  local shares = {}
3243  local share_details = {}
3244
3245  -- Try and do this the good way, make a MSRPC call to get the shares
3246  stdnse.debug1("SMB: Attempting to log into the system to enumerate shares")
3247  enum_status, shares = msrpc.enum_shares(host)
3248
3249  -- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the
3250  -- best we can do.
3251  if(enum_status == false) then
3252    stdnse.debug1("SMB: Enumerating shares failed, guessing at common ones (%s)", shares)
3253    extra = string.format("ERROR: Enumerating shares failed, guessing at common ones (%s)", shares)
3254
3255    -- Take some common share names I've seen (thanks to Brandon Enright for most of these, except the last few)
3256    shares = {"ADMIN", "BACKUP", "DATA", "DESKTOP", "DOCS", "FILES", "GROUPS", "HD", "HOME", "INFO", "IPC", "MEDIA", "MY DOCUMENTS", "NETLOGON", "PICTURES", "PORN", "PR0N", "PRINT", "PROGRAMS", "PRON", "PUBLIC", "SHARE", "SHARED", "SOFTWARE", "STMP", "TEMP", "TEST", "TMP", "USERS", "WEB DOCUMENTS","WEBSERVER", "WWW", "XSERVE" }
3257
3258    -- Try every alphabetic share
3259    for i = string.byte("A", 1), string.byte("Z", 1), 1 do
3260      shares[#shares + 1] = string.char(i)
3261    end
3262
3263    -- For each share, add one with the same name and a trailing '$'
3264    local sharesLength = #shares
3265    for shareItr = 1, sharesLength, 1 do
3266      shares[ sharesLength + shareItr ] = shares[ shareItr ] .. '$'
3267    end
3268  else
3269    stdnse.debug1("SMB: Found %d shares, will attempt to find more information", #shares)
3270  end
3271
3272  -- Sort the shares
3273  table.sort(shares)
3274
3275  -- Ensure that the server returns the proper error message
3276  -- first try anonymously, then using a user account (in case anonymous connections are not supported)
3277  for _, anon in ipairs({true, false}) do
3278    status, result = share_host_returns_proper_error(host, anon)
3279
3280    if(status == true and result == false) then
3281      return false, "Server doesn't return proper value for non-existent shares; can't enumerate shares"
3282    end
3283  end
3284
3285  if(status == false) then
3286    return false, result
3287  end
3288
3289  -- Get more information on each share
3290  for i = 1, #shares, 1 do
3291    local status, result
3292    stdnse.debug1("SMB: Getting information for share: %s", shares[i])
3293    status, result = share_get_details(host, shares[i])
3294    if(status == false and result == 'NT_STATUS_BAD_NETWORK_NAME') then
3295      stdnse.debug1("SMB: Share doesn't exist: %s", shares[i])
3296    elseif(status == false) then
3297      stdnse.debug1("SMB: Error while getting share details: %s", result)
3298      return false, result
3299    else
3300      -- Save the share details
3301      table.insert(share_details, result)
3302    end
3303  end
3304
3305  return true, share_details, extra
3306end
3307
3308---Find a share that the current user can write to. Return it, along with its path. If no share could be found,
3309-- an error is returned. If the path cannot be determined, the returned path is nil.
3310--
3311--@param host The host object.
3312--@return (status, name, path, names) If status is false, result is an error message. Otherwise, name is the name of the share,
3313--        path is its path, if it could be determined, and names is a list of all writable shares.
3314function share_find_writable(host)
3315  local i
3316  local status, shares
3317  local main_name, main_path
3318  local names = {}
3319  local writable = {}
3320
3321  status, shares = share_get_list(host)
3322  if(status == false) then
3323    return false, shares
3324  end
3325
3326  for i = 1, #shares, 1 do
3327    if(shares[i]['user_can_write'] == true) then
3328      if(main_name == nil) then
3329        main_name = shares[i]['name']
3330
3331        if(shares[i]['details'] ~= nil) then
3332          main_path = shares[i]['details']['path']
3333        end
3334      end
3335
3336      table.insert(names, shares[i]['name'])
3337    end
3338  end
3339
3340  if(main_name == nil) then
3341    return false, "Couldn't find a writable share!"
3342  else
3343    return true, main_name, main_path, names
3344  end
3345end
3346
3347--- Converts numbered Windows version strings (<code>"Windows 5.0"</code>, <code>"Windows 5.1"</code>) to names (<code>"Windows 2000"</code>, <code>"Windows XP"</code>).
3348--@param os The numbered OS version.
3349--@return The actual name of the OS (or the same as the <code>os</code> parameter if no match was found).
3350function get_windows_version(os)
3351
3352  if(os == "Windows 5.0") then
3353    return "Windows 2000"
3354  elseif(os == "Windows 5.1")then
3355    return "Windows XP"
3356  end
3357
3358  return os
3359
3360end
3361
3362---Retrieve information about the host's operating system. This should always be possible to call, as long as there isn't already
3363-- a SMB session established.
3364--
3365-- The returned table has the following keys (shown here with sample values).
3366-- * <code>os</code>: <code>"Windows 7 Professional 7601 Service Pack 1"</code>
3367-- * <code>lanmanager</code>: <code>"Windows 7 Professional 6.1"</code>
3368-- * <code>domain</code>: <code>"WORKGROUP"</code>
3369-- * <code>server</code>: <code>"COMPUTERNAME"</code>
3370-- * <code>time</code>: <code>1347121470.0462</code>
3371-- * <code>date</code>: <code>"2012-09-08 09:24:30"</code>
3372-- * <code>timezone</code>: <code>-7</code>
3373-- * <code>timezone_str</code>: <code>UTC-7</code>
3374-- * <code>port</code>: <code>445</code>
3375-- The table may also contain these additional keys:
3376-- * <code>fqdn</code>: <code>"Sql2008.lab.test.local"</code>
3377-- * <code>domain_dns</code>: <code>"lab.test.local"</code>
3378-- * <code>forest_dns</code>: <code>"test.local"</code>
3379-- * <code>workgroup</code>
3380--
3381--@param host The host object
3382--@return (status, data) If status is true, data is a table of values; otherwise, data is an error message.
3383function get_os(host)
3384  local state
3385  local status, smbstate
3386
3387  local response = {}
3388
3389  -- Start up SMB
3390  status, smbstate = start_ex(host, true, true, nil, nil, true)
3391  if(status == false) then
3392    return false, smbstate
3393  end
3394
3395  -- See if we actually got something
3396  if(smbstate['os'] == nil and smbstate['lanmanager'] == nil) then
3397    return false, "Server didn't return OS details"
3398  end
3399
3400  response['os']           = smbstate['os']
3401  response['lanmanager']   = smbstate['lanmanager']
3402  response['domain']       = smbstate['domain']
3403  response['server']       = smbstate['server']
3404  response['date']         = smbstate['date']
3405  response['time']         = smbstate['time']
3406  response['timezone_str'] = smbstate['timezone_str']
3407  response['timezone']     = smbstate['timezone']
3408  response['port']         = smbstate['port']
3409
3410  -- Kill SMB
3411  stop(smbstate)
3412
3413
3414  -- Start another session with extended security. This will allow us to get
3415  -- additional information about the target.
3416  status, smbstate = start_ex(host, true, true, nil, nil, false)
3417  if(status == true) then
3418    -- See if we actually got something
3419    if (smbstate['fqdn'] or smbstate['domain_dns'] or smbstate['forest_dns']) then
3420      response['fqdn']         = smbstate['fqdn']
3421      response['domain_dns']   = smbstate['domain_dns']
3422      response['forest_dns']   = smbstate['forest_dns']
3423      -- After a non-extended security negotiation, smbstate['domain'] will
3424      -- contain the NetBIOS domain name, or the workgroup name. However,
3425      -- after an extended-security session setup, smbstate['domain'] will
3426      -- contain the NetBIOS domain name. For hosts in a workgroup, Windows
3427      -- uses the NetBIOS hostname as the NetBIOS domain name. Comparing the
3428      -- two will reveal whether the target is in a domain or a workgroup.
3429      if ( smbstate['domain'] ~= nil and response['domain'] ~= smbstate['domain'] ) then
3430        response['workgroup']    = response['domain']
3431        response['domain']       = nil
3432      end
3433    end
3434
3435    -- Kill SMB again
3436    stop(smbstate)
3437  end
3438
3439  return true, response
3440end
3441
3442---Basically a wrapper around <code>socket:get_info</code>, except that it also makes a SMB connection before calling the
3443-- <code>get_info</code> function. Returns the mac address as well, for convenience.
3444--
3445--@param host The host object
3446--@return status: true for successful, false otherwise.
3447--@return If status is true, the local ip address; otherwise, an error message.
3448--@return The local port (not really meaningful, since it'll change next time).
3449--@return The remote ip address.
3450--@return The report port.
3451--@return The mac address, if possible; nil otherwise.
3452function get_socket_info(host)
3453  local status, lhost, lport, rhost, rport
3454  local smbstate, socket
3455
3456  -- Start SMB (we need a socket to get the proper local ip
3457  status, smbstate = start_ex(host)
3458  if(status == false) then
3459    return false, smbstate
3460  end
3461
3462  socket = smbstate['socket']
3463  status, lhost, lport, rhost, rport = socket:get_info()
3464  if(status == false) then
3465    return false, lhost
3466  end
3467
3468  -- Stop SMB
3469  stop(smbstate)
3470
3471  -- Get the mac in hex format, if possible
3472  local lmac = nil
3473  if(host.mac_addr_src) then
3474    lmac = stdnse.tohex(host.mac_addr_src, {separator = ":"})
3475  end
3476
3477  return true, lhost, lport, rhost, rport, lmac
3478end
3479
3480---Generate a string that's somewhat unique, but is based on factors that won't
3481-- change on a host.
3482--
3483-- At the moment, this is a very simple hash based on the IP address. This hash
3484-- is *very* likely to have collisions, and that's by design -- while it should
3485-- be somewhat unique, I don't want it to be trivial to uniquely determine who
3486-- it originated from.
3487--
3488-- TODO: At some point, I should re-do this function properly, with a method of
3489-- hashing that's somewhat proven.
3490--
3491--@param host      The host object
3492--@param extension [optional] The extension to add on the end of the file.
3493--                 Default: none.
3494--@param seed [optional] Some randomness on which to base the name. If you want
3495--            to do multiple files, each with its own uniqueish name, this can
3496--            be used.
3497--@return (status, data) If status is true, data is a table of values;
3498--        otherwise, data is an error message. Can be any kind of string.
3499function get_uniqueish_name(host, extension, seed)
3500
3501  local status
3502  local lhost, lport, rhost, rport
3503  if(type(host) == "table") then
3504    status, lhost = get_socket_info(host)
3505  else
3506    lhost = host
3507  end
3508
3509  -- Create our ultra-weak hash by using a simple xor/shift algorithm
3510  -- I tested this, and in 255 tests, there were roughly 10 collisions. That's about what I'm looking for.
3511  local hash = 0
3512  local i
3513  local str = lhost .. (seed or "") .. (extension or "") .. (nmap.registry.args.randomseed or "")
3514
3515  for i = 1, #str, 1 do
3516    local chr = str:byte(i)
3517    hash = hash ~ chr
3518    hash = (hash << 3) | (hash >> 29)
3519    hash = hash ~ 3
3520    hash = hash & 0xFFFFFFFF
3521  end
3522
3523  local response
3524  if(extension) then
3525    response = string.format("%x.%s", hash, extension)
3526  else
3527    response = string.format("%x", hash)
3528  end
3529
3530  return true, response
3531end
3532
3533---Determines, as accurately as possible, whether or not an account is an administrator. If there is an error,
3534-- 'false' is simply returned.
3535function is_admin(host, username, domain, password, password_hash, hash_type)
3536  local msrpc = require "msrpc" -- avoid require cycle
3537  local overrides = get_overrides(username, domain, password, password_hash, hash_type)
3538
3539  stdnse.debug1("SMB: Checking if %s is an administrator", username)
3540
3541  local status, smbstate = start(host)
3542  if(status == false) then
3543    stdnse.debug1("SMB; is_admin: Failed to start SMB: %s [%s]", smbstate, username)
3544    stop(smbstate)
3545    return false
3546  end
3547
3548  local status, err      = negotiate_protocol(smbstate, overrides)
3549  if(status == false) then
3550    stdnse.debug1("SMB; is_admin: Failed to negotiate protocol: %s [%s]", err, username)
3551    stop(smbstate)
3552    return false
3553  end
3554
3555  status, err      = start_session(smbstate, overrides)
3556  if(status == false) then
3557    stdnse.debug1("SMB; is_admin: Failed to start session %s [%s]", err, username)
3558    stop(smbstate)
3559    return false
3560  end
3561
3562  local _, fqpn_share = get_fqpn(host, "IPC$")
3563  status, err      = tree_connect(smbstate, fqpn_share, overrides)
3564  if(status == false) then
3565    stdnse.debug1("SMB; is_admin: Failed to connect tree: %s [%s]", err, username)
3566    stop(smbstate)
3567    return false
3568  end
3569
3570  status, err      = create_file(smbstate, msrpc.SRVSVC_PATH, overrides)
3571  if(status == false) then
3572    stdnse.debug1("SMB; is_admin: Failed to create file: %s [%s]", err, username)
3573    stop(smbstate)
3574    return false
3575  end
3576
3577  status, err      = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
3578  if(status == false) then
3579    stdnse.debug1("SMB; is_admin: Failed to bind: %s [%s]", err, username)
3580    stop(smbstate)
3581    return false
3582  end
3583
3584  -- Call netservergetstatistics for 'server'
3585  status, err = msrpc.srvsvc_netservergetstatistics(smbstate, host.ip)
3586  if(status == false) then
3587    stdnse.debug1("SMB; is_admin: Couldn't get server stats (may be normal): %s [%s]", err, username)
3588    stop(smbstate)
3589    return false
3590  end
3591
3592  stop(smbstate)
3593
3594  return true
3595end
3596
3597---
3598-- Returns the fully qualified path name (FQPN) for shares.
3599-- This is required for modern versions of Windows.
3600-- Returns \\<ip>\<sharename> when successful. Otherwise, returns the same share name.
3601---
3602function get_fqpn(host, sharename)
3603  if host.ip and sharename then
3604    return true, string.format("\\\\%s\\%s", host.ip, sharename)
3605  end
3606  stdnse.debug1("SMB: get_fqpn: Couldn't determine server IP address")
3607  return false, sharename
3608end
3609
3610command_codes =
3611{
3612  SMB_COM_CREATE_DIRECTORY          = 0x00,
3613  SMB_COM_DELETE_DIRECTORY          = 0x01,
3614  SMB_COM_OPEN                      = 0x02,
3615  SMB_COM_CREATE                    = 0x03,
3616  SMB_COM_CLOSE                     = 0x04,
3617  SMB_COM_FLUSH                     = 0x05,
3618  SMB_COM_DELETE                    = 0x06,
3619  SMB_COM_RENAME                    = 0x07,
3620  SMB_COM_QUERY_INFORMATION         = 0x08,
3621  SMB_COM_SET_INFORMATION           = 0x09,
3622  SMB_COM_READ                      = 0x0A,
3623  SMB_COM_WRITE                     = 0x0B,
3624  SMB_COM_LOCK_BYTE_RANGE           = 0x0C,
3625  SMB_COM_UNLOCK_BYTE_RANGE         = 0x0D,
3626  SMB_COM_CREATE_TEMPORARY          = 0x0E,
3627  SMB_COM_CREATE_NEW                = 0x0F,
3628  SMB_COM_CHECK_DIRECTORY           = 0x10,
3629  SMB_COM_PROCESS_EXIT              = 0x11,
3630  SMB_COM_SEEK                      = 0x12,
3631  SMB_COM_LOCK_AND_READ             = 0x13,
3632  SMB_COM_WRITE_AND_UNLOCK          = 0x14,
3633  SMB_COM_READ_RAW                  = 0x1A,
3634  SMB_COM_READ_MPX                  = 0x1B,
3635  SMB_COM_READ_MPX_SECONDARY        = 0x1C,
3636  SMB_COM_WRITE_RAW                 = 0x1D,
3637  SMB_COM_WRITE_MPX                 = 0x1E,
3638  SMB_COM_WRITE_MPX_SECONDARY       = 0x1F,
3639  SMB_COM_WRITE_COMPLETE            = 0x20,
3640  SMB_COM_QUERY_SERVER              = 0x21,
3641  SMB_COM_SET_INFORMATION2          = 0x22,
3642  SMB_COM_QUERY_INFORMATION2        = 0x23,
3643  SMB_COM_LOCKING_ANDX              = 0x24,
3644  SMB_COM_TRANSACTION               = 0x25,
3645  SMB_COM_TRANSACTION_SECONDARY     = 0x26,
3646  SMB_COM_IOCTL                     = 0x27,
3647  SMB_COM_IOCTL_SECONDARY           = 0x28,
3648  SMB_COM_COPY                      = 0x29,
3649  SMB_COM_MOVE                      = 0x2A,
3650  SMB_COM_ECHO                      = 0x2B,
3651  SMB_COM_WRITE_AND_CLOSE           = 0x2C,
3652  SMB_COM_OPEN_ANDX                 = 0x2D,
3653  SMB_COM_READ_ANDX                 = 0x2E,
3654  SMB_COM_WRITE_ANDX                = 0x2F,
3655  SMB_COM_NEW_FILE_SIZE             = 0x30,
3656  SMB_COM_CLOSE_AND_TREE_DISC       = 0x31,
3657  SMB_COM_TRANSACTION2              = 0x32,
3658  SMB_COM_TRANSACTION2_SECONDARY    = 0x33,
3659  SMB_COM_FIND_CLOSE2               = 0x34,
3660  SMB_COM_FIND_NOTIFY_CLOSE         = 0x35,
3661  SMB_COM_TREE_CONNECT              = 0x70,
3662  SMB_COM_TREE_DISCONNECT           = 0x71,
3663  SMB_COM_NEGOTIATE                 = 0x72,
3664  SMB_COM_SESSION_SETUP_ANDX        = 0x73,
3665  SMB_COM_LOGOFF_ANDX               = 0x74,
3666  SMB_COM_TREE_CONNECT_ANDX         = 0x75,
3667  SMB_COM_QUERY_INFORMATION_DISK    = 0x80,
3668  SMB_COM_SEARCH                    = 0x81,
3669  SMB_COM_FIND                      = 0x82,
3670  SMB_COM_FIND_UNIQUE               = 0x83,
3671  SMB_COM_FIND_CLOSE                = 0x84,
3672  SMB_COM_NT_TRANSACT               = 0xA0,
3673  SMB_COM_NT_TRANSACT_SECONDARY     = 0xA1,
3674  SMB_COM_NT_CREATE_ANDX            = 0xA2,
3675  SMB_COM_NT_CANCEL                 = 0xA4,
3676  SMB_COM_NT_RENAME                 = 0xA5,
3677  SMB_COM_OPEN_PRINT_FILE           = 0xC0,
3678  SMB_COM_WRITE_PRINT_FILE          = 0xC1,
3679  SMB_COM_CLOSE_PRINT_FILE          = 0xC2,
3680  SMB_COM_GET_PRINT_QUEUE           = 0xC3,
3681  SMB_COM_READ_BULK                 = 0xD8,
3682  SMB_COM_WRITE_BULK                = 0xD9,
3683  SMB_COM_WRITE_BULK_DATA           = 0xDA,
3684  SMB_NO_FURTHER_COMMANDS           = 0xFF
3685}
3686
3687for i, v in pairs(command_codes) do
3688  command_names[v] = i
3689end
3690
3691
3692-- https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/2198f480-e047-4df0-ba64-f28eadef00b9
3693file_attributes =
3694{
3695  SMB_FILE_ATTRIBUTE_NORMAL      = 0x0000,
3696  SMB_FILE_ATTRIBUTE_READONLY    = 0x0001,
3697  SMB_FILE_ATTRIBUTE_HIDDEN      = 0x0002,
3698  SMB_FILE_ATTRIBUTE_SYSTEM      = 0x0004,
3699  SMB_FILE_ATTRIBUTE_VOLUME      = 0x0008,
3700  SMB_FILE_ATTRIBUTE_DIRECTORY   = 0x0010,
3701  SMB_FILE_ATTRIBUTE_ARCHIVE     = 0x0020,
3702  SMB_SEARCH_ATTRIBUTE_READONLY  = 0x0100,
3703  SMB_SEARCH_ATTRIBUTE_HIDDEN    = 0x0200,
3704  SMB_SEARCH_ATTRIBUTE_SYSTEM    = 0x0400,
3705  SMB_SEARCH_ATTRIBUTE_DIRECTORY = 0x1000,
3706  SMB_SEARCH_ATTRIBUTE_ARCHIVE   = 0x2000
3707}
3708
3709
3710-- see http://msdn.microsoft.com/en-us/library/cc231196(v=prot.10).aspx
3711status_codes =
3712{
3713  NT_STATUS_SUCCESS                           = 0x00000000,
3714  NT_STATUS_WERR_BADFILE                      = 0x00000002,
3715  NT_STATUS_WERR_ACCESS_DENIED                = 0x00000005,
3716  NT_STATUS_WERR_INVALID_PARAMETER            = 0x00000057,
3717  NT_STATUS_WERR_INVALID_NAME                 = 0x0000007b,
3718  NT_STATUS_WERR_UNKNOWN_LEVEL                = 0x0000007c,
3719  NT_STATUS_WERR_MORE_DATA                    = 0x000000ea,
3720  NT_STATUS_NO_MORE_ITEMS                     = 0x00000103,
3721  NT_STATUS_MORE_ENTRIES                      = 0x00000105,
3722  NT_STATUS_SOME_NOT_MAPPED                   = 0x00000107,
3723  NT_STATUS_SERVICE_REQUEST_TIMEOUT           = 0x0000041D,
3724  NT_STATUS_SERVICE_NO_THREAD                 = 0x0000041E,
3725  NT_STATUS_SERVICE_DATABASE_LOCKED           = 0x0000041F,
3726  NT_STATUS_SERVICE_ALREADY_RUNNING           = 0x00000420,
3727  NT_STATUS_INVALID_SERVICE_ACCOUNT           = 0x00000421,
3728  NT_STATUS_SERVICE_DISABLED                  = 0x00000422,
3729  NT_STATUS_CIRCULAR_DEPENDENCY               = 0x00000423,
3730  NT_STATUS_SERVICE_DOES_NOT_EXIST            = 0x00000424,
3731  NT_STATUS_SERVICE_CANNOT_ACCEPT_CTRL        = 0x00000425,
3732  NT_STATUS_SERVICE_NOT_ACTIVE                = 0x00000426,
3733  NT_STATUS_FAILED_SERVICE_CONTROLLER_CONNECT = 0x00000427,
3734  NT_STATUS_EXCEPTION_IN_SERVICE              = 0x00000428,
3735  NT_STATUS_DATABASE_DOES_NOT_EXIST           = 0x00000429,
3736  NT_STATUS_SERVICE_SPECIFIC_ERROR            = 0x0000042a,
3737  NT_STATUS_PROCESS_ABORTED                   = 0x0000042b,
3738  NT_STATUS_SERVICE_DEPENDENCY_FAIL           = 0x0000042c,
3739  NT_STATUS_SERVICE_LOGON_FAILED              = 0x0000042d,
3740  NT_STATUS_SERVICE_START_HANG                = 0x0000042e,
3741  NT_STATUS_INVALID_SERVICE_LOCK              = 0x0000042f,
3742  NT_STATUS_SERVICE_MARKED_FOR_DELETE         = 0x00000430,
3743  NT_STATUS_SERVICE_EXISTS                    = 0x00000431,
3744  NT_STATUS_ALREADY_RUNNING_LKG               = 0x00000432,
3745  NT_STATUS_SERVICE_DEPENDENCY_DELETED        = 0x00000433,
3746  NT_STATUS_BOOT_ALREADY_ACCEPTED             = 0x00000434,
3747  NT_STATUS_SERVICE_NEVER_STARTED             = 0x00000435,
3748  NT_STATUS_DUPLICATE_SERVICE_NAME            = 0x00000436,
3749  NT_STATUS_DIFFERENT_SERVICE_ACCOUNT         = 0x00000437,
3750  NT_STATUS_CANNOT_DETECT_DRIVER_FAILURE      = 0x00000438,
3751  DOS_STATUS_UNKNOWN_ERROR                    = 0x00010001,
3752  DOS_STATUS_NONSPECIFIC_ERROR                = 0x00010002,
3753  DOS_STATUS_DIRECTORY_NOT_FOUND              = 0x00030001,
3754  DOS_STATUS_ACCESS_DENIED                    = 0x00050001,
3755  DOS_STATUS_INVALID_FID                      = 0x00060001,
3756  DOS_STATUS_INVALID_NETWORK_NAME             = 0x00060002,
3757  NT_STATUS_BUFFER_OVERFLOW                   = 0x80000005,
3758  NT_STATUS_UNSUCCESSFUL                      = 0xc0000001,
3759  NT_STATUS_NOT_IMPLEMENTED                   = 0xc0000002,
3760  NT_STATUS_INVALID_INFO_CLASS                = 0xc0000003,
3761  NT_STATUS_INFO_LENGTH_MISMATCH              = 0xc0000004,
3762  NT_STATUS_ACCESS_VIOLATION                  = 0xc0000005,
3763  NT_STATUS_IN_PAGE_ERROR                     = 0xc0000006,
3764  NT_STATUS_PAGEFILE_QUOTA                    = 0xc0000007,
3765  NT_STATUS_INVALID_HANDLE                    = 0xc0000008,
3766  NT_STATUS_BAD_INITIAL_STACK                 = 0xc0000009,
3767  NT_STATUS_BAD_INITIAL_PC                    = 0xc000000a,
3768  NT_STATUS_INVALID_CID                       = 0xc000000b,
3769  NT_STATUS_TIMER_NOT_CANCELED                = 0xc000000c,
3770  NT_STATUS_INVALID_PARAMETER                 = 0xc000000d,
3771  NT_STATUS_NO_SUCH_DEVICE                    = 0xc000000e,
3772  NT_STATUS_NO_SUCH_FILE                      = 0xc000000f,
3773  NT_STATUS_INVALID_DEVICE_REQUEST            = 0xc0000010,
3774  NT_STATUS_END_OF_FILE                       = 0xc0000011,
3775  NT_STATUS_WRONG_VOLUME                      = 0xc0000012,
3776  NT_STATUS_NO_MEDIA_IN_DEVICE                = 0xc0000013,
3777  NT_STATUS_UNRECOGNIZED_MEDIA                = 0xc0000014,
3778  NT_STATUS_NONEXISTENT_SECTOR                = 0xc0000015,
3779  NT_STATUS_MORE_PROCESSING_REQUIRED          = 0xc0000016,
3780  NT_STATUS_NO_MEMORY                         = 0xc0000017,
3781  NT_STATUS_CONFLICTING_ADDRESSES             = 0xc0000018,
3782  NT_STATUS_NOT_MAPPED_VIEW                   = 0xc0000019,
3783  NT_STATUS_UNABLE_TO_FREE_VM                 = 0xc000001a,
3784  NT_STATUS_UNABLE_TO_DELETE_SECTION          = 0xc000001b,
3785  NT_STATUS_INVALID_SYSTEM_SERVICE            = 0xc000001c,
3786  NT_STATUS_ILLEGAL_INSTRUCTION               = 0xc000001d,
3787  NT_STATUS_INVALID_LOCK_SEQUENCE             = 0xc000001e,
3788  NT_STATUS_INVALID_VIEW_SIZE                 = 0xc000001f,
3789  NT_STATUS_INVALID_FILE_FOR_SECTION          = 0xc0000020,
3790  NT_STATUS_ALREADY_COMMITTED                 = 0xc0000021,
3791  NT_STATUS_ACCESS_DENIED                     = 0xc0000022,
3792  NT_STATUS_BUFFER_TOO_SMALL                  = 0xc0000023,
3793  NT_STATUS_OBJECT_TYPE_MISMATCH              = 0xc0000024,
3794  NT_STATUS_NONCONTINUABLE_EXCEPTION          = 0xc0000025,
3795  NT_STATUS_INVALID_DISPOSITION               = 0xc0000026,
3796  NT_STATUS_UNWIND                            = 0xc0000027,
3797  NT_STATUS_BAD_STACK                         = 0xc0000028,
3798  NT_STATUS_INVALID_UNWIND_TARGET             = 0xc0000029,
3799  NT_STATUS_NOT_LOCKED                        = 0xc000002a,
3800  NT_STATUS_PARITY_ERROR                      = 0xc000002b,
3801  NT_STATUS_UNABLE_TO_DECOMMIT_VM             = 0xc000002c,
3802  NT_STATUS_NOT_COMMITTED                     = 0xc000002d,
3803  NT_STATUS_INVALID_PORT_ATTRIBUTES           = 0xc000002e,
3804  NT_STATUS_PORT_MESSAGE_TOO_LONG             = 0xc000002f,
3805  NT_STATUS_INVALID_PARAMETER_MIX             = 0xc0000030,
3806  NT_STATUS_INVALID_QUOTA_LOWER               = 0xc0000031,
3807  NT_STATUS_DISK_CORRUPT_ERROR                = 0xc0000032,
3808  NT_STATUS_OBJECT_NAME_INVALID               = 0xc0000033,
3809  NT_STATUS_OBJECT_NAME_NOT_FOUND             = 0xc0000034,
3810  NT_STATUS_OBJECT_NAME_COLLISION             = 0xc0000035,
3811  NT_STATUS_HANDLE_NOT_WAITABLE               = 0xc0000036,
3812  NT_STATUS_PORT_DISCONNECTED                 = 0xc0000037,
3813  NT_STATUS_DEVICE_ALREADY_ATTACHED           = 0xc0000038,
3814  NT_STATUS_OBJECT_PATH_INVALID               = 0xc0000039,
3815  NT_STATUS_OBJECT_PATH_NOT_FOUND             = 0xc000003a,
3816  NT_STATUS_OBJECT_PATH_SYNTAX_BAD            = 0xc000003b,
3817  NT_STATUS_DATA_OVERRUN                      = 0xc000003c,
3818  NT_STATUS_DATA_LATE_ERROR                   = 0xc000003d,
3819  NT_STATUS_DATA_ERROR                        = 0xc000003e,
3820  NT_STATUS_CRC_ERROR                         = 0xc000003f,
3821  NT_STATUS_SECTION_TOO_BIG                   = 0xc0000040,
3822  NT_STATUS_PORT_CONNECTION_REFUSED           = 0xc0000041,
3823  NT_STATUS_INVALID_PORT_HANDLE               = 0xc0000042,
3824  NT_STATUS_SHARING_VIOLATION                 = 0xc0000043,
3825  NT_STATUS_QUOTA_EXCEEDED                    = 0xc0000044,
3826  NT_STATUS_INVALID_PAGE_PROTECTION           = 0xc0000045,
3827  NT_STATUS_MUTANT_NOT_OWNED                  = 0xc0000046,
3828  NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED          = 0xc0000047,
3829  NT_STATUS_PORT_ALREADY_SET                  = 0xc0000048,
3830  NT_STATUS_SECTION_NOT_IMAGE                 = 0xc0000049,
3831  NT_STATUS_SUSPEND_COUNT_EXCEEDED            = 0xc000004a,
3832  NT_STATUS_THREAD_IS_TERMINATING             = 0xc000004b,
3833  NT_STATUS_BAD_WORKING_SET_LIMIT             = 0xc000004c,
3834  NT_STATUS_INCOMPATIBLE_FILE_MAP             = 0xc000004d,
3835  NT_STATUS_SECTION_PROTECTION                = 0xc000004e,
3836  NT_STATUS_EAS_NOT_SUPPORTED                 = 0xc000004f,
3837  NT_STATUS_EA_TOO_LARGE                      = 0xc0000050,
3838  NT_STATUS_NONEXISTENT_EA_ENTRY              = 0xc0000051,
3839  NT_STATUS_NO_EAS_ON_FILE                    = 0xc0000052,
3840  NT_STATUS_EA_CORRUPT_ERROR                  = 0xc0000053,
3841  NT_STATUS_FILE_LOCK_CONFLICT                = 0xc0000054,
3842  NT_STATUS_LOCK_NOT_GRANTED                  = 0xc0000055,
3843  NT_STATUS_DELETE_PENDING                    = 0xc0000056,
3844  NT_STATUS_CTL_FILE_NOT_SUPPORTED            = 0xc0000057,
3845  NT_STATUS_UNKNOWN_REVISION                  = 0xc0000058,
3846  NT_STATUS_REVISION_MISMATCH                 = 0xc0000059,
3847  NT_STATUS_INVALID_OWNER                     = 0xc000005a,
3848  NT_STATUS_INVALID_PRIMARY_GROUP             = 0xc000005b,
3849  NT_STATUS_NO_IMPERSONATION_TOKEN            = 0xc000005c,
3850  NT_STATUS_CANT_DISABLE_MANDATORY            = 0xc000005d,
3851  NT_STATUS_NO_LOGON_SERVERS                  = 0xc000005e,
3852  NT_STATUS_NO_SUCH_LOGON_SESSION             = 0xc000005f,
3853  NT_STATUS_NO_SUCH_PRIVILEGE                 = 0xc0000060,
3854  NT_STATUS_PRIVILEGE_NOT_HELD                = 0xc0000061,
3855  NT_STATUS_INVALID_ACCOUNT_NAME              = 0xc0000062,
3856  NT_STATUS_USER_EXISTS                       = 0xc0000063,
3857  NT_STATUS_NO_SUCH_USER                      = 0xc0000064,
3858  NT_STATUS_GROUP_EXISTS                      = 0xc0000065,
3859  NT_STATUS_NO_SUCH_GROUP                     = 0xc0000066,
3860  NT_STATUS_MEMBER_IN_GROUP                   = 0xc0000067,
3861  NT_STATUS_MEMBER_NOT_IN_GROUP               = 0xc0000068,
3862  NT_STATUS_LAST_ADMIN                        = 0xc0000069,
3863  NT_STATUS_WRONG_PASSWORD                    = 0xc000006a,
3864  NT_STATUS_ILL_FORMED_PASSWORD               = 0xc000006b,
3865  NT_STATUS_PASSWORD_RESTRICTION              = 0xc000006c,
3866  NT_STATUS_LOGON_FAILURE                     = 0xc000006d,
3867  NT_STATUS_ACCOUNT_RESTRICTION               = 0xc000006e,
3868  NT_STATUS_INVALID_LOGON_HOURS               = 0xc000006f,
3869  NT_STATUS_INVALID_WORKSTATION               = 0xc0000070,
3870  NT_STATUS_PASSWORD_EXPIRED                  = 0xc0000071,
3871  NT_STATUS_ACCOUNT_DISABLED                  = 0xc0000072,
3872  NT_STATUS_NONE_MAPPED                       = 0xc0000073,
3873  NT_STATUS_TOO_MANY_LUIDS_REQUESTED          = 0xc0000074,
3874  NT_STATUS_LUIDS_EXHAUSTED                   = 0xc0000075,
3875  NT_STATUS_INVALID_SUB_AUTHORITY             = 0xc0000076,
3876  NT_STATUS_INVALID_ACL                       = 0xc0000077,
3877  NT_STATUS_INVALID_SID                       = 0xc0000078,
3878  NT_STATUS_INVALID_SECURITY_DESCR            = 0xc0000079,
3879  NT_STATUS_PROCEDURE_NOT_FOUND               = 0xc000007a,
3880  NT_STATUS_INVALID_IMAGE_FORMAT              = 0xc000007b,
3881  NT_STATUS_NO_TOKEN                          = 0xc000007c,
3882  NT_STATUS_BAD_INHERITANCE_ACL               = 0xc000007d,
3883  NT_STATUS_RANGE_NOT_LOCKED                  = 0xc000007e,
3884  NT_STATUS_DISK_FULL                         = 0xc000007f,
3885  NT_STATUS_SERVER_DISABLED                   = 0xc0000080,
3886  NT_STATUS_SERVER_NOT_DISABLED               = 0xc0000081,
3887  NT_STATUS_TOO_MANY_GUIDS_REQUESTED          = 0xc0000082,
3888  NT_STATUS_GUIDS_EXHAUSTED                   = 0xc0000083,
3889  NT_STATUS_INVALID_ID_AUTHORITY              = 0xc0000084,
3890  NT_STATUS_AGENTS_EXHAUSTED                  = 0xc0000085,
3891  NT_STATUS_INVALID_VOLUME_LABEL              = 0xc0000086,
3892  NT_STATUS_SECTION_NOT_EXTENDED              = 0xc0000087,
3893  NT_STATUS_NOT_MAPPED_DATA                   = 0xc0000088,
3894  NT_STATUS_RESOURCE_DATA_NOT_FOUND           = 0xc0000089,
3895  NT_STATUS_RESOURCE_TYPE_NOT_FOUND           = 0xc000008a,
3896  NT_STATUS_RESOURCE_NAME_NOT_FOUND           = 0xc000008b,
3897  NT_STATUS_ARRAY_BOUNDS_EXCEEDED             = 0xc000008c,
3898  NT_STATUS_FLOAT_DENORMAL_OPERAND            = 0xc000008d,
3899  NT_STATUS_FLOAT_DIVIDE_BY_ZERO              = 0xc000008e,
3900  NT_STATUS_FLOAT_INEXACT_RESULT              = 0xc000008f,
3901  NT_STATUS_FLOAT_INVALID_OPERATION           = 0xc0000090,
3902  NT_STATUS_FLOAT_OVERFLOW                    = 0xc0000091,
3903  NT_STATUS_FLOAT_STACK_CHECK                 = 0xc0000092,
3904  NT_STATUS_FLOAT_UNDERFLOW                   = 0xc0000093,
3905  NT_STATUS_INTEGER_DIVIDE_BY_ZERO            = 0xc0000094,
3906  NT_STATUS_INTEGER_OVERFLOW                  = 0xc0000095,
3907  NT_STATUS_PRIVILEGED_INSTRUCTION            = 0xc0000096,
3908  NT_STATUS_TOO_MANY_PAGING_FILES             = 0xc0000097,
3909  NT_STATUS_FILE_INVALID                      = 0xc0000098,
3910  NT_STATUS_ALLOTTED_SPACE_EXCEEDED           = 0xc0000099,
3911  NT_STATUS_INSUFFICIENT_RESOURCES            = 0xc000009a,
3912  NT_STATUS_DFS_EXIT_PATH_FOUND               = 0xc000009b,
3913  NT_STATUS_DEVICE_DATA_ERROR                 = 0xc000009c,
3914  NT_STATUS_DEVICE_NOT_CONNECTED              = 0xc000009d,
3915  NT_STATUS_DEVICE_POWER_FAILURE              = 0xc000009e,
3916  NT_STATUS_FREE_VM_NOT_AT_BASE               = 0xc000009f,
3917  NT_STATUS_MEMORY_NOT_ALLOCATED              = 0xc00000a0,
3918  NT_STATUS_WORKING_SET_QUOTA                 = 0xc00000a1,
3919  NT_STATUS_MEDIA_WRITE_PROTECTED             = 0xc00000a2,
3920  NT_STATUS_DEVICE_NOT_READY                  = 0xc00000a3,
3921  NT_STATUS_INVALID_GROUP_ATTRIBUTES          = 0xc00000a4,
3922  NT_STATUS_BAD_IMPERSONATION_LEVEL           = 0xc00000a5,
3923  NT_STATUS_CANT_OPEN_ANONYMOUS               = 0xc00000a6,
3924  NT_STATUS_BAD_VALIDATION_CLASS              = 0xc00000a7,
3925  NT_STATUS_BAD_TOKEN_TYPE                    = 0xc00000a8,
3926  NT_STATUS_BAD_MASTER_BOOT_RECORD            = 0xc00000a9,
3927  NT_STATUS_INSTRUCTION_MISALIGNMENT          = 0xc00000aa,
3928  NT_STATUS_INSTANCE_NOT_AVAILABLE            = 0xc00000ab,
3929  NT_STATUS_PIPE_NOT_AVAILABLE                = 0xc00000ac,
3930  NT_STATUS_INVALID_PIPE_STATE                = 0xc00000ad,
3931  NT_STATUS_PIPE_BUSY                         = 0xc00000ae,
3932  NT_STATUS_ILLEGAL_FUNCTION                  = 0xc00000af,
3933  NT_STATUS_PIPE_DISCONNECTED                 = 0xc00000b0,
3934  NT_STATUS_PIPE_CLOSING                      = 0xc00000b1,
3935  NT_STATUS_PIPE_CONNECTED                    = 0xc00000b2,
3936  NT_STATUS_PIPE_LISTENING                    = 0xc00000b3,
3937  NT_STATUS_INVALID_READ_MODE                 = 0xc00000b4,
3938  NT_STATUS_IO_TIMEOUT                        = 0xc00000b5,
3939  NT_STATUS_FILE_FORCED_CLOSED                = 0xc00000b6,
3940  NT_STATUS_PROFILING_NOT_STARTED             = 0xc00000b7,
3941  NT_STATUS_PROFILING_NOT_STOPPED             = 0xc00000b8,
3942  NT_STATUS_COULD_NOT_INTERPRET               = 0xc00000b9,
3943  NT_STATUS_FILE_IS_A_DIRECTORY               = 0xc00000ba,
3944  NT_STATUS_NOT_SUPPORTED                     = 0xc00000bb,
3945  NT_STATUS_REMOTE_NOT_LISTENING              = 0xc00000bc,
3946  NT_STATUS_DUPLICATE_NAME                    = 0xc00000bd,
3947  NT_STATUS_BAD_NETWORK_PATH                  = 0xc00000be,
3948  NT_STATUS_NETWORK_BUSY                      = 0xc00000bf,
3949  NT_STATUS_DEVICE_DOES_NOT_EXIST             = 0xc00000c0,
3950  NT_STATUS_TOO_MANY_COMMANDS                 = 0xc00000c1,
3951  NT_STATUS_ADAPTER_HARDWARE_ERROR            = 0xc00000c2,
3952  NT_STATUS_INVALID_NETWORK_RESPONSE          = 0xc00000c3,
3953  NT_STATUS_UNEXPECTED_NETWORK_ERROR          = 0xc00000c4,
3954  NT_STATUS_BAD_REMOTE_ADAPTER                = 0xc00000c5,
3955  NT_STATUS_PRINT_QUEUE_FULL                  = 0xc00000c6,
3956  NT_STATUS_NO_SPOOL_SPACE                    = 0xc00000c7,
3957  NT_STATUS_PRINT_CANCELLED                   = 0xc00000c8,
3958  NT_STATUS_NETWORK_NAME_DELETED              = 0xc00000c9,
3959  NT_STATUS_NETWORK_ACCESS_DENIED             = 0xc00000ca,
3960  NT_STATUS_BAD_DEVICE_TYPE                   = 0xc00000cb,
3961  NT_STATUS_BAD_NETWORK_NAME                  = 0xc00000cc,
3962  NT_STATUS_TOO_MANY_NAMES                    = 0xc00000cd,
3963  NT_STATUS_TOO_MANY_SESSIONS                 = 0xc00000ce,
3964  NT_STATUS_SHARING_PAUSED                    = 0xc00000cf,
3965  NT_STATUS_REQUEST_NOT_ACCEPTED              = 0xc00000d0,
3966  NT_STATUS_REDIRECTOR_PAUSED                 = 0xc00000d1,
3967  NT_STATUS_NET_WRITE_FAULT                   = 0xc00000d2,
3968  NT_STATUS_PROFILING_AT_LIMIT                = 0xc00000d3,
3969  NT_STATUS_NOT_SAME_DEVICE                   = 0xc00000d4,
3970  NT_STATUS_FILE_RENAMED                      = 0xc00000d5,
3971  NT_STATUS_VIRTUAL_CIRCUIT_CLOSED            = 0xc00000d6,
3972  NT_STATUS_NO_SECURITY_ON_OBJECT             = 0xc00000d7,
3973  NT_STATUS_CANT_WAIT                         = 0xc00000d8,
3974  NT_STATUS_PIPE_EMPTY                        = 0xc00000d9,
3975  NT_STATUS_CANT_ACCESS_DOMAIN_INFO           = 0xc00000da,
3976  NT_STATUS_CANT_TERMINATE_SELF               = 0xc00000db,
3977  NT_STATUS_INVALID_SERVER_STATE              = 0xc00000dc,
3978  NT_STATUS_INVALID_DOMAIN_STATE              = 0xc00000dd,
3979  NT_STATUS_INVALID_DOMAIN_ROLE               = 0xc00000de,
3980  NT_STATUS_NO_SUCH_DOMAIN                    = 0xc00000df,
3981  NT_STATUS_DOMAIN_EXISTS                     = 0xc00000e0,
3982  NT_STATUS_DOMAIN_LIMIT_EXCEEDED             = 0xc00000e1,
3983  NT_STATUS_OPLOCK_NOT_GRANTED                = 0xc00000e2,
3984  NT_STATUS_INVALID_OPLOCK_PROTOCOL           = 0xc00000e3,
3985  NT_STATUS_INTERNAL_DB_CORRUPTION            = 0xc00000e4,
3986  NT_STATUS_INTERNAL_ERROR                    = 0xc00000e5,
3987  NT_STATUS_GENERIC_NOT_MAPPED                = 0xc00000e6,
3988  NT_STATUS_BAD_DESCRIPTOR_FORMAT             = 0xc00000e7,
3989  NT_STATUS_INVALID_USER_BUFFER               = 0xc00000e8,
3990  NT_STATUS_UNEXPECTED_IO_ERROR               = 0xc00000e9,
3991  NT_STATUS_UNEXPECTED_MM_CREATE_ERR          = 0xc00000ea,
3992  NT_STATUS_UNEXPECTED_MM_MAP_ERROR           = 0xc00000eb,
3993  NT_STATUS_UNEXPECTED_MM_EXTEND_ERR          = 0xc00000ec,
3994  NT_STATUS_NOT_LOGON_PROCESS                 = 0xc00000ed,
3995  NT_STATUS_LOGON_SESSION_EXISTS              = 0xc00000ee,
3996  NT_STATUS_INVALID_PARAMETER_1               = 0xc00000ef,
3997  NT_STATUS_INVALID_PARAMETER_2               = 0xc00000f0,
3998  NT_STATUS_INVALID_PARAMETER_3               = 0xc00000f1,
3999  NT_STATUS_INVALID_PARAMETER_4               = 0xc00000f2,
4000  NT_STATUS_INVALID_PARAMETER_5               = 0xc00000f3,
4001  NT_STATUS_INVALID_PARAMETER_6               = 0xc00000f4,
4002  NT_STATUS_INVALID_PARAMETER_7               = 0xc00000f5,
4003  NT_STATUS_INVALID_PARAMETER_8               = 0xc00000f6,
4004  NT_STATUS_INVALID_PARAMETER_9               = 0xc00000f7,
4005  NT_STATUS_INVALID_PARAMETER_10              = 0xc00000f8,
4006  NT_STATUS_INVALID_PARAMETER_11              = 0xc00000f9,
4007  NT_STATUS_INVALID_PARAMETER_12              = 0xc00000fa,
4008  NT_STATUS_REDIRECTOR_NOT_STARTED            = 0xc00000fb,
4009  NT_STATUS_REDIRECTOR_STARTED                = 0xc00000fc,
4010  NT_STATUS_STACK_OVERFLOW                    = 0xc00000fd,
4011  NT_STATUS_NO_SUCH_PACKAGE                   = 0xc00000fe,
4012  NT_STATUS_BAD_FUNCTION_TABLE                = 0xc00000ff,
4013  NT_STATUS_DIRECTORY_NOT_EMPTY               = 0xc0000101,
4014  NT_STATUS_FILE_CORRUPT_ERROR                = 0xc0000102,
4015  NT_STATUS_NOT_A_DIRECTORY                   = 0xc0000103,
4016  NT_STATUS_BAD_LOGON_SESSION_STATE           = 0xc0000104,
4017  NT_STATUS_LOGON_SESSION_COLLISION           = 0xc0000105,
4018  NT_STATUS_NAME_TOO_LONG                     = 0xc0000106,
4019  NT_STATUS_FILES_OPEN                        = 0xc0000107,
4020  NT_STATUS_CONNECTION_IN_USE                 = 0xc0000108,
4021  NT_STATUS_MESSAGE_NOT_FOUND                 = 0xc0000109,
4022  NT_STATUS_PROCESS_IS_TERMINATING            = 0xc000010a,
4023  NT_STATUS_INVALID_LOGON_TYPE                = 0xc000010b,
4024  NT_STATUS_NO_GUID_TRANSLATION               = 0xc000010c,
4025  NT_STATUS_CANNOT_IMPERSONATE                = 0xc000010d,
4026  NT_STATUS_IMAGE_ALREADY_LOADED              = 0xc000010e,
4027  NT_STATUS_ABIOS_NOT_PRESENT                 = 0xc000010f,
4028  NT_STATUS_ABIOS_LID_NOT_EXIST               = 0xc0000110,
4029  NT_STATUS_ABIOS_LID_ALREADY_OWNED           = 0xc0000111,
4030  NT_STATUS_ABIOS_NOT_LID_OWNER               = 0xc0000112,
4031  NT_STATUS_ABIOS_INVALID_COMMAND             = 0xc0000113,
4032  NT_STATUS_ABIOS_INVALID_LID                 = 0xc0000114,
4033  NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE      = 0xc0000115,
4034  NT_STATUS_ABIOS_INVALID_SELECTOR            = 0xc0000116,
4035  NT_STATUS_NO_LDT                            = 0xc0000117,
4036  NT_STATUS_INVALID_LDT_SIZE                  = 0xc0000118,
4037  NT_STATUS_INVALID_LDT_OFFSET                = 0xc0000119,
4038  NT_STATUS_INVALID_LDT_DESCRIPTOR            = 0xc000011a,
4039  NT_STATUS_INVALID_IMAGE_NE_FORMAT           = 0xc000011b,
4040  NT_STATUS_RXACT_INVALID_STATE               = 0xc000011c,
4041  NT_STATUS_RXACT_COMMIT_FAILURE              = 0xc000011d,
4042  NT_STATUS_MAPPED_FILE_SIZE_ZERO             = 0xc000011e,
4043  NT_STATUS_TOO_MANY_OPENED_FILES             = 0xc000011f,
4044  NT_STATUS_CANCELLED                         = 0xc0000120,
4045  NT_STATUS_CANNOT_DELETE                     = 0xc0000121,
4046  NT_STATUS_INVALID_COMPUTER_NAME             = 0xc0000122,
4047  NT_STATUS_FILE_DELETED                      = 0xc0000123,
4048  NT_STATUS_SPECIAL_ACCOUNT                   = 0xc0000124,
4049  NT_STATUS_SPECIAL_GROUP                     = 0xc0000125,
4050  NT_STATUS_SPECIAL_USER                      = 0xc0000126,
4051  NT_STATUS_MEMBERS_PRIMARY_GROUP             = 0xc0000127,
4052  NT_STATUS_FILE_CLOSED                       = 0xc0000128,
4053  NT_STATUS_TOO_MANY_THREADS                  = 0xc0000129,
4054  NT_STATUS_THREAD_NOT_IN_PROCESS             = 0xc000012a,
4055  NT_STATUS_TOKEN_ALREADY_IN_USE              = 0xc000012b,
4056  NT_STATUS_PAGEFILE_QUOTA_EXCEEDED           = 0xc000012c,
4057  NT_STATUS_COMMITMENT_LIMIT                  = 0xc000012d,
4058  NT_STATUS_INVALID_IMAGE_LE_FORMAT           = 0xc000012e,
4059  NT_STATUS_INVALID_IMAGE_NOT_MZ              = 0xc000012f,
4060  NT_STATUS_INVALID_IMAGE_PROTECT             = 0xc0000130,
4061  NT_STATUS_INVALID_IMAGE_WIN_16              = 0xc0000131,
4062  NT_STATUS_LOGON_SERVER_CONFLICT             = 0xc0000132,
4063  NT_STATUS_TIME_DIFFERENCE_AT_DC             = 0xc0000133,
4064  NT_STATUS_SYNCHRONIZATION_REQUIRED          = 0xc0000134,
4065  NT_STATUS_DLL_NOT_FOUND                     = 0xc0000135,
4066  NT_STATUS_OPEN_FAILED                       = 0xc0000136,
4067  NT_STATUS_IO_PRIVILEGE_FAILED               = 0xc0000137,
4068  NT_STATUS_ORDINAL_NOT_FOUND                 = 0xc0000138,
4069  NT_STATUS_ENTRYPOINT_NOT_FOUND              = 0xc0000139,
4070  NT_STATUS_CONTROL_C_EXIT                    = 0xc000013a,
4071  NT_STATUS_LOCAL_DISCONNECT                  = 0xc000013b,
4072  NT_STATUS_REMOTE_DISCONNECT                 = 0xc000013c,
4073  NT_STATUS_REMOTE_RESOURCES                  = 0xc000013d,
4074  NT_STATUS_LINK_FAILED                       = 0xc000013e,
4075  NT_STATUS_LINK_TIMEOUT                      = 0xc000013f,
4076  NT_STATUS_INVALID_CONNECTION                = 0xc0000140,
4077  NT_STATUS_INVALID_ADDRESS                   = 0xc0000141,
4078  NT_STATUS_DLL_INIT_FAILED                   = 0xc0000142,
4079  NT_STATUS_MISSING_SYSTEMFILE                = 0xc0000143,
4080  NT_STATUS_UNHANDLED_EXCEPTION               = 0xc0000144,
4081  NT_STATUS_APP_INIT_FAILURE                  = 0xc0000145,
4082  NT_STATUS_PAGEFILE_CREATE_FAILED            = 0xc0000146,
4083  NT_STATUS_NO_PAGEFILE                       = 0xc0000147,
4084  NT_STATUS_INVALID_LEVEL                     = 0xc0000148,
4085  NT_STATUS_WRONG_PASSWORD_CORE               = 0xc0000149,
4086  NT_STATUS_ILLEGAL_FLOAT_CONTEXT             = 0xc000014a,
4087  NT_STATUS_PIPE_BROKEN                       = 0xc000014b,
4088  NT_STATUS_REGISTRY_CORRUPT                  = 0xc000014c,
4089  NT_STATUS_REGISTRY_IO_FAILED                = 0xc000014d,
4090  NT_STATUS_NO_EVENT_PAIR                     = 0xc000014e,
4091  NT_STATUS_UNRECOGNIZED_VOLUME               = 0xc000014f,
4092  NT_STATUS_SERIAL_NO_DEVICE_INITED           = 0xc0000150,
4093  NT_STATUS_NO_SUCH_ALIAS                     = 0xc0000151,
4094  NT_STATUS_MEMBER_NOT_IN_ALIAS               = 0xc0000152,
4095  NT_STATUS_MEMBER_IN_ALIAS                   = 0xc0000153,
4096  NT_STATUS_ALIAS_EXISTS                      = 0xc0000154,
4097  NT_STATUS_LOGON_NOT_GRANTED                 = 0xc0000155,
4098  NT_STATUS_TOO_MANY_SECRETS                  = 0xc0000156,
4099  NT_STATUS_SECRET_TOO_LONG                   = 0xc0000157,
4100  NT_STATUS_INTERNAL_DB_ERROR                 = 0xc0000158,
4101  NT_STATUS_FULLSCREEN_MODE                   = 0xc0000159,
4102  NT_STATUS_TOO_MANY_CONTEXT_IDS              = 0xc000015a,
4103  NT_STATUS_LOGON_TYPE_NOT_GRANTED            = 0xc000015b,
4104  NT_STATUS_NOT_REGISTRY_FILE                 = 0xc000015c,
4105  NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED      = 0xc000015d,
4106  NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR         = 0xc000015e,
4107  NT_STATUS_FT_MISSING_MEMBER                 = 0xc000015f,
4108  NT_STATUS_ILL_FORMED_SERVICE_ENTRY          = 0xc0000160,
4109  NT_STATUS_ILLEGAL_CHARACTER                 = 0xc0000161,
4110  NT_STATUS_UNMAPPABLE_CHARACTER              = 0xc0000162,
4111  NT_STATUS_UNDEFINED_CHARACTER               = 0xc0000163,
4112  NT_STATUS_FLOPPY_VOLUME                     = 0xc0000164,
4113  NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND          = 0xc0000165,
4114  NT_STATUS_FLOPPY_WRONG_CYLINDER             = 0xc0000166,
4115  NT_STATUS_FLOPPY_UNKNOWN_ERROR              = 0xc0000167,
4116  NT_STATUS_FLOPPY_BAD_REGISTERS              = 0xc0000168,
4117  NT_STATUS_DISK_RECALIBRATE_FAILED           = 0xc0000169,
4118  NT_STATUS_DISK_OPERATION_FAILED             = 0xc000016a,
4119  NT_STATUS_DISK_RESET_FAILED                 = 0xc000016b,
4120  NT_STATUS_SHARED_IRQ_BUSY                   = 0xc000016c,
4121  NT_STATUS_FT_ORPHANING                      = 0xc000016d,
4122  NT_STATUS_PARTITION_FAILURE                 = 0xc0000172,
4123  NT_STATUS_INVALID_BLOCK_LENGTH              = 0xc0000173,
4124  NT_STATUS_DEVICE_NOT_PARTITIONED            = 0xc0000174,
4125  NT_STATUS_UNABLE_TO_LOCK_MEDIA              = 0xc0000175,
4126  NT_STATUS_UNABLE_TO_UNLOAD_MEDIA            = 0xc0000176,
4127  NT_STATUS_EOM_OVERFLOW                      = 0xc0000177,
4128  NT_STATUS_NO_MEDIA                          = 0xc0000178,
4129  NT_STATUS_NO_SUCH_MEMBER                    = 0xc000017a,
4130  NT_STATUS_INVALID_MEMBER                    = 0xc000017b,
4131  NT_STATUS_KEY_DELETED                       = 0xc000017c,
4132  NT_STATUS_NO_LOG_SPACE                      = 0xc000017d,
4133  NT_STATUS_TOO_MANY_SIDS                     = 0xc000017e,
4134  NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED      = 0xc000017f,
4135  NT_STATUS_KEY_HAS_CHILDREN                  = 0xc0000180,
4136  NT_STATUS_CHILD_MUST_BE_VOLATILE            = 0xc0000181,
4137  NT_STATUS_DEVICE_CONFIGURATION_ERROR        = 0xc0000182,
4138  NT_STATUS_DRIVER_INTERNAL_ERROR             = 0xc0000183,
4139  NT_STATUS_INVALID_DEVICE_STATE              = 0xc0000184,
4140  NT_STATUS_IO_DEVICE_ERROR                   = 0xc0000185,
4141  NT_STATUS_DEVICE_PROTOCOL_ERROR             = 0xc0000186,
4142  NT_STATUS_BACKUP_CONTROLLER                 = 0xc0000187,
4143  NT_STATUS_LOG_FILE_FULL                     = 0xc0000188,
4144  NT_STATUS_TOO_LATE                          = 0xc0000189,
4145  NT_STATUS_NO_TRUST_LSA_SECRET               = 0xc000018a,
4146  NT_STATUS_NO_TRUST_SAM_ACCOUNT              = 0xc000018b,
4147  NT_STATUS_TRUSTED_DOMAIN_FAILURE            = 0xc000018c,
4148  NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE      = 0xc000018d,
4149  NT_STATUS_EVENTLOG_FILE_CORRUPT             = 0xc000018e,
4150  NT_STATUS_EVENTLOG_CANT_START               = 0xc000018f,
4151  NT_STATUS_TRUST_FAILURE                     = 0xc0000190,
4152  NT_STATUS_MUTANT_LIMIT_EXCEEDED             = 0xc0000191,
4153  NT_STATUS_NETLOGON_NOT_STARTED              = 0xc0000192,
4154  NT_STATUS_ACCOUNT_EXPIRED                   = 0xc0000193,
4155  NT_STATUS_POSSIBLE_DEADLOCK                 = 0xc0000194,
4156  NT_STATUS_NETWORK_CREDENTIAL_CONFLICT       = 0xc0000195,
4157  NT_STATUS_REMOTE_SESSION_LIMIT              = 0xc0000196,
4158  NT_STATUS_EVENTLOG_FILE_CHANGED             = 0xc0000197,
4159  NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xc0000198,
4160  NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xc0000199,
4161  NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT      = 0xc000019a,
4162  NT_STATUS_DOMAIN_TRUST_INCONSISTENT         = 0xc000019b,
4163  NT_STATUS_FS_DRIVER_REQUIRED                = 0xc000019c,
4164  NT_STATUS_NO_USER_SESSION_KEY               = 0xc0000202,
4165  NT_STATUS_USER_SESSION_DELETED              = 0xc0000203,
4166  NT_STATUS_RESOURCE_LANG_NOT_FOUND           = 0xc0000204,
4167  NT_STATUS_INSUFF_SERVER_RESOURCES           = 0xc0000205,
4168  NT_STATUS_INVALID_BUFFER_SIZE               = 0xc0000206,
4169  NT_STATUS_INVALID_ADDRESS_COMPONENT         = 0xc0000207,
4170  NT_STATUS_INVALID_ADDRESS_WILDCARD          = 0xc0000208,
4171  NT_STATUS_TOO_MANY_ADDRESSES                = 0xc0000209,
4172  NT_STATUS_ADDRESS_ALREADY_EXISTS            = 0xc000020a,
4173  NT_STATUS_ADDRESS_CLOSED                    = 0xc000020b,
4174  NT_STATUS_CONNECTION_DISCONNECTED           = 0xc000020c,
4175  NT_STATUS_CONNECTION_RESET                  = 0xc000020d,
4176  NT_STATUS_TOO_MANY_NODES                    = 0xc000020e,
4177  NT_STATUS_TRANSACTION_ABORTED               = 0xc000020f,
4178  NT_STATUS_TRANSACTION_TIMED_OUT             = 0xc0000210,
4179  NT_STATUS_TRANSACTION_NO_RELEASE            = 0xc0000211,
4180  NT_STATUS_TRANSACTION_NO_MATCH              = 0xc0000212,
4181  NT_STATUS_TRANSACTION_RESPONDED             = 0xc0000213,
4182  NT_STATUS_TRANSACTION_INVALID_ID            = 0xc0000214,
4183  NT_STATUS_TRANSACTION_INVALID_TYPE          = 0xc0000215,
4184  NT_STATUS_NOT_SERVER_SESSION                = 0xc0000216,
4185  NT_STATUS_NOT_CLIENT_SESSION                = 0xc0000217,
4186  NT_STATUS_CANNOT_LOAD_REGISTRY_FILE         = 0xc0000218,
4187  NT_STATUS_DEBUG_ATTACH_FAILED               = 0xc0000219,
4188  NT_STATUS_SYSTEM_PROCESS_TERMINATED         = 0xc000021a,
4189  NT_STATUS_DATA_NOT_ACCEPTED                 = 0xc000021b,
4190  NT_STATUS_NO_BROWSER_SERVERS_FOUND          = 0xc000021c,
4191  NT_STATUS_VDM_HARD_ERROR                    = 0xc000021d,
4192  NT_STATUS_DRIVER_CANCEL_TIMEOUT             = 0xc000021e,
4193  NT_STATUS_REPLY_MESSAGE_MISMATCH            = 0xc000021f,
4194  NT_STATUS_MAPPED_ALIGNMENT                  = 0xc0000220,
4195  NT_STATUS_IMAGE_CHECKSUM_MISMATCH           = 0xc0000221,
4196  NT_STATUS_LOST_WRITEBEHIND_DATA             = 0xc0000222,
4197  NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID  = 0xc0000223,
4198  NT_STATUS_PASSWORD_MUST_CHANGE              = 0xc0000224,
4199  NT_STATUS_NOT_FOUND                         = 0xc0000225,
4200  NT_STATUS_NOT_TINY_STREAM                   = 0xc0000226,
4201  NT_STATUS_RECOVERY_FAILURE                  = 0xc0000227,
4202  NT_STATUS_STACK_OVERFLOW_READ               = 0xc0000228,
4203  NT_STATUS_FAIL_CHECK                        = 0xc0000229,
4204  NT_STATUS_DUPLICATE_OBJECTID                = 0xc000022a,
4205  NT_STATUS_OBJECTID_EXISTS                   = 0xc000022b,
4206  NT_STATUS_CONVERT_TO_LARGE                  = 0xc000022c,
4207  NT_STATUS_RETRY                             = 0xc000022d,
4208  NT_STATUS_FOUND_OUT_OF_SCOPE                = 0xc000022e,
4209  NT_STATUS_ALLOCATE_BUCKET                   = 0xc000022f,
4210  NT_STATUS_PROPSET_NOT_FOUND                 = 0xc0000230,
4211  NT_STATUS_MARSHALL_OVERFLOW                 = 0xc0000231,
4212  NT_STATUS_INVALID_VARIANT                   = 0xc0000232,
4213  NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND       = 0xc0000233,
4214  NT_STATUS_ACCOUNT_LOCKED_OUT                = 0xc0000234,
4215  NT_STATUS_HANDLE_NOT_CLOSABLE               = 0xc0000235,
4216  NT_STATUS_CONNECTION_REFUSED                = 0xc0000236,
4217  NT_STATUS_GRACEFUL_DISCONNECT               = 0xc0000237,
4218  NT_STATUS_ADDRESS_ALREADY_ASSOCIATED        = 0xc0000238,
4219  NT_STATUS_ADDRESS_NOT_ASSOCIATED            = 0xc0000239,
4220  NT_STATUS_CONNECTION_INVALID                = 0xc000023a,
4221  NT_STATUS_CONNECTION_ACTIVE                 = 0xc000023b,
4222  NT_STATUS_NETWORK_UNREACHABLE               = 0xc000023c,
4223  NT_STATUS_HOST_UNREACHABLE                  = 0xc000023d,
4224  NT_STATUS_PROTOCOL_UNREACHABLE              = 0xc000023e,
4225  NT_STATUS_PORT_UNREACHABLE                  = 0xc000023f,
4226  NT_STATUS_REQUEST_ABORTED                   = 0xc0000240,
4227  NT_STATUS_CONNECTION_ABORTED                = 0xc0000241,
4228  NT_STATUS_BAD_COMPRESSION_BUFFER            = 0xc0000242,
4229  NT_STATUS_USER_MAPPED_FILE                  = 0xc0000243,
4230  NT_STATUS_AUDIT_FAILED                      = 0xc0000244,
4231  NT_STATUS_TIMER_RESOLUTION_NOT_SET          = 0xc0000245,
4232  NT_STATUS_CONNECTION_COUNT_LIMIT            = 0xc0000246,
4233  NT_STATUS_LOGIN_TIME_RESTRICTION            = 0xc0000247,
4234  NT_STATUS_LOGIN_WKSTA_RESTRICTION           = 0xc0000248,
4235  NT_STATUS_IMAGE_MP_UP_MISMATCH              = 0xc0000249,
4236  NT_STATUS_INSUFFICIENT_LOGON_INFO           = 0xc0000250,
4237  NT_STATUS_BAD_DLL_ENTRYPOINT                = 0xc0000251,
4238  NT_STATUS_BAD_SERVICE_ENTRYPOINT            = 0xc0000252,
4239  NT_STATUS_LPC_REPLY_LOST                    = 0xc0000253,
4240  NT_STATUS_IP_ADDRESS_CONFLICT1              = 0xc0000254,
4241  NT_STATUS_IP_ADDRESS_CONFLICT2              = 0xc0000255,
4242  NT_STATUS_REGISTRY_QUOTA_LIMIT              = 0xc0000256,
4243  NT_STATUS_PATH_NOT_COVERED                  = 0xc0000257,
4244  NT_STATUS_NO_CALLBACK_ACTIVE                = 0xc0000258,
4245  NT_STATUS_LICENSE_QUOTA_EXCEEDED            = 0xc0000259,
4246  NT_STATUS_PWD_TOO_SHORT                     = 0xc000025a,
4247  NT_STATUS_PWD_TOO_RECENT                    = 0xc000025b,
4248  NT_STATUS_PWD_HISTORY_CONFLICT              = 0xc000025c,
4249  NT_STATUS_PLUGPLAY_NO_DEVICE                = 0xc000025e,
4250  NT_STATUS_UNSUPPORTED_COMPRESSION           = 0xc000025f,
4251  NT_STATUS_INVALID_HW_PROFILE                = 0xc0000260,
4252  NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH      = 0xc0000261,
4253  NT_STATUS_DRIVER_ORDINAL_NOT_FOUND          = 0xc0000262,
4254  NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND       = 0xc0000263,
4255  NT_STATUS_RESOURCE_NOT_OWNED                = 0xc0000264,
4256  NT_STATUS_TOO_MANY_LINKS                    = 0xc0000265,
4257  NT_STATUS_QUOTA_LIST_INCONSISTENT           = 0xc0000266,
4258  NT_STATUS_FILE_IS_OFFLINE                   = 0xc0000267,
4259  NT_STATUS_DS_NO_MORE_RIDS                   = 0xc00002a8,
4260  NT_STATUS_NOT_A_REPARSE_POINT               = 0xc0000275,
4261  NT_STATUS_NO_SUCH_JOB                       = 0xc0000EDE
4262}
4263
4264for i, v in pairs(status_codes) do
4265  status_names[v] = i
4266end
4267
4268
4269local NP_LIBRARY_NAME = "PIPE"
4270
4271namedpipes =
4272{
4273  get_pipe_subpath = function( pipeName, writeToDebugLog )
4274    local status, pipeSubPath
4275    if not pipeName then return false end
4276
4277    local _, _, match = pipeName:match( "^(\\+)(.-)\\pipe(\\.-)$" )
4278    if match then
4279      pipeSubPath = match
4280      status = true
4281      if writeToDebugLog then
4282        stdnse.debug2("%s: Converting %s to subpath %s", NP_LIBRARY_NAME, pipeName, match )
4283      end
4284    else
4285      status = false
4286      pipeSubPath = pipeName
4287    end
4288
4289    return status, pipeSubPath
4290  end,
4291
4292
4293  make_pipe_name = function( hostnameOrIp, pipeSubPath )
4294    if pipeSubPath:sub(1,1) ~= "\\" then
4295      pipeSubPath = "\\" .. pipeSubPath
4296    end
4297
4298    return string.format( "\\\\%s\\pipe%s", hostnameOrIp, pipeSubPath )
4299  end,
4300
4301
4302  named_pipe = {
4303
4304    _smbstate = nil,
4305    _host = nil,
4306    _pipeSubPath = nil,
4307    _overrides = nil,
4308    name = nil,
4309
4310    new = function(self,o)
4311      o = o or {}
4312      setmetatable(o, self)
4313      self.__index = self
4314      return o
4315    end,
4316
4317
4318    connect = function( self, host, pipeSubPath, overrides )
4319
4320      stdnse.debug2("%s: connect() called with %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) )
4321      self._overrides = overrides or {}
4322      self._host = host
4323      self._pipeSubPath = pipeSubPath
4324      if not host and not host.ip then return false, "host table is required" end
4325      if not pipeSubPath then return false, "pipeSubPath is required" end
4326
4327      -- If we got a full pipe name, not a sub-path, fix it
4328      if ( pipeSubPath:match( "^\\\\(.-)$" ) ) then
4329        local status
4330        status, self._pipeSubPath = namedpipes.get_pipe_subpath( self._pipeSubPath, true )
4331        if ( not status ) then
4332          stdnse.debug1("%s: Attempt to connect to invalid pipe name: %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) )
4333          return false, "Invalid pipe name"
4334        end
4335      end
4336      self.name = namedpipes.make_pipe_name( self._host.ip, self._pipeSubPath )
4337
4338      stdnse.debug2("%s: Connecting to named pipe: %s", NP_LIBRARY_NAME, self.name )
4339      local errorMessage
4340      local bool_negotiate_protocol, bool_start_session, bool_disable_extended = true, true, false
4341      local _, fqpn_share = get_fqpn(host, "IPC$")
4342      local status, result = start_ex( self._host, bool_negotiate_protocol, bool_start_session,
4343        fqpn_share, self._pipeSubPath, bool_disable_extended, self._overrides )
4344
4345      if status then
4346        self._smbstate = result
4347      else
4348        errorMessage = string.format( "Connection failed: %s", result )
4349        stdnse.debug2("%s: Connection to named pipe (%s) failed: %s",
4350          NP_LIBRARY_NAME, self.name, errorMessage )
4351      end
4352
4353      return status, errorMessage, result
4354    end,
4355
4356
4357    disconnect = function( self )
4358      if ( self._smbstate ) then
4359        stdnse.debug2("%s: Disconnecting named pipe: %s", NP_LIBRARY_NAME, self.name )
4360        return stop( self._smbstate )
4361      else
4362        stdnse.debug2("%s: disconnect() called, but SMB connection is already closed: %s", NP_LIBRARY_NAME, self.name )
4363      end
4364    end,
4365
4366
4367    send = function( self, messageData )
4368      if not self._smbstate then
4369        stdnse.debug2("%s: send() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name )
4370        return false, "Failed to send message on named pipe"
4371      end
4372
4373      local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity
4374      local status, result, errorMessage
4375
4376      status, result = write_file( self._smbstate, messageData, offset, self._overrides )
4377
4378      -- if status is true, result is data that we don't need to pay attention to
4379      if not status then
4380        stdnse.debug2("%s: Write to named pipe (%s) failed: %s",
4381          NP_LIBRARY_NAME, self.name, result )
4382        errorMessage = "Failed to send message on named pipe", result
4383      end
4384
4385      return status, errorMessage
4386    end,
4387
4388
4389    receive = function( self )
4390      if not self._smbstate then
4391        stdnse.debug2("%s: receive() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name )
4392        return false, "Failed to read from named pipe"
4393      end
4394
4395      local status, result, messageData
4396      -- Packet header values
4397      local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity
4398      local MAX_BYTES_PER_READ = 4096
4399
4400      status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides )
4401
4402      if status and result.data then
4403        messageData = result.data
4404      else
4405        stdnse.debug2("%s: Read from named pipe (%s) failed: %s",
4406          NP_LIBRARY_NAME, self.name, result )
4407        return false, "Failed to read from named pipe", result
4408      end
4409
4410      while (result["status"] == status_codes.NT_STATUS_BUFFER_OVERFLOW) do
4411        status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides )
4412
4413        if status and result.data then
4414          messageData = messageData .. result.data
4415        else
4416          stdnse.debug2("%s: Read additional data from named pipe (%s) failed: %s",
4417            NP_LIBRARY_NAME, self.name, result )
4418          return false, "Failed to read from named pipe", result
4419        end
4420      end
4421
4422      return status, messageData
4423    end,
4424  }
4425
4426}
4427
4428filetype_codes =
4429{
4430  FILE_TYPE_DISK              = 0x00,
4431  FILE_TYPE_BYTE_MODE_PIPE    = 0x01,
4432  FILE_TYPE_MESSAGE_MODE_PIPE = 0x02,
4433  FILE_TYPE_PRINTER           = 0x03,
4434  FILE_TYPE_UNKNOWN           = 0xFF
4435}
4436
4437for i, v in pairs(filetype_codes) do
4438  filetype_names[v] = i
4439end
4440
4441return _ENV;
4442