##############################################################################
#
#  SMB Functions
#
#  Copyright: SecPod
#
#  Date Written: 2008/08/13
#
#  Revision: 1.1
#
#  Log: bchandra
#
#  Added a support to detect file version with space and coma. (By schandan).
#  2008/09/16: Updated with new functions. (bchandra)
#
#  Substring dump error fix. 2009/04/28, 14:45:23 (By schandan).
#  ------------------------------------------------------------------------
#  This program was written by SecPod and is licensed under the GNU GPL
#  license. Please refer to the below link for details,
#  http://www.gnu.org/licenses/gpl.html
#  This header contains information regarding licensing terms under the GPL,
#  and information regarding obtaining source code from the Author.
#  Consequently, pursuant to section 3(c) of the GPL, you must accompany the
#  information found in this header with any distribution you make of this
#  Program.
#  ------------------------------------------------------------------------
##############################################################################

###############################################################################
# GetVer(), bin_word(), bin_dword() are a derivative of smbcl_func.inc available
# in the OpenVAS repository
# This script was written by Carsten Koch-Mauthe <c.koch-mauthe at dn-systems.de>
#
# This script is released under the GNU GPLv2
###############################################################################

#
# Get File version or product version reading through a file. An
# offset can be sent indicating the start location. By default
# file version is read, if product version is required, we can
# indicate verstr="prod"
#

function GetVersion(socket, uid, tid, fid, offset, verstr, debug)
{

  local_var fsize, data, i, offset, tmp, version, ver, vers, len, retVal;

  pattern = "F?i?l?e?V?e?r?s?i?o?n";
  if(verstr == "prod"){
    pattern = "P?r?o?d?u?c?t?V?e?r?s?i?o?n";
  }

  else if(verstr){
   pattern = verstr;
  }

  patlen = strlen(pattern);

  fsize = smb_get_file_size(socket:socket, uid:uid, tid:tid, fid:fid);

  if(isnull(offset))
  {
    if(fsize < 180224){
      offset = 0;
    }
    else{
      offset = fsize - 180224;
    }
  }

  if(offset < 0){
     offset = fsize + offset;
  }

  start = offset;

  if(start < 0 || start > fsize){
    start = fsize/2;
  }

  offset = start;
  chunk = 16384;

  for(i = 0; offset < fsize; i++)
  {
    tmp = ReadAndX(socket:socket, uid:uid, tid:tid, fid:fid, count:chunk, off:offset);
    if(tmp)
    {
      if(pattern == "build"){
        tmp = str_replace(find:raw_string(0), replace:"", string:tmp);
      }
      else
        tmp = str_replace(find:raw_string(0), replace:"?", string:tmp);

      data += tmp;
      version = strstr(data, pattern);
      if(version)
      {
        len = strlen(version);
        for(i = patlen; i < len; i++)
        {
          if((ord(version[i]) < ord("0") || ord(version[i]) > ord("9")) &&
             (version[i] != "." && version[i] != "," && version[i] != " " &&
              version[i] != "?"))
          {
            if(ver[strlen(ver)-1] == ".")
            {
              vers = split(ver, sep:".", keep:0);
              foreach item (vers){
                retVal += string("." + item);
              }
              retVal -= string(".");
                return(retVal);
            }
            return (ver);
          }
          else if(version[i] == "," || version[i] == "."){
            ver += ".";
          }
          else if(ver && version[i] == "?" && version[i+1] == "?"){
            return (ver);
          }
          else if(version[i] == " "||version[i] == "?"){
          }
          else{
            ver += version[i];
          }
        }
      }
      offset += chunk;
    }
  }
  return NULL;
}


global_var name, login, pass, domain, port;

name    =  kb_smb_name();
login   =  kb_smb_login();
pass    =  kb_smb_password();
domain  =  kb_smb_domain();
port    =  get_kb_item("SMB/transport");


########################################################################
# Enumerate registry values for a given key
# param:
#	key: REGISTRY_KEY
#
########################################################################

function registry_enum_values(key)
{
        if(!port){
                port = 445;
        }

        soc = open_sock_tcp(port);
        if(!soc){
                return FALSE;
        }

        r = smb_session_request(soc:soc, remote:name);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        prot = smb_neg_prot(soc:soc);
        if(!prot)
        {
                close(soc);
                return FALSE;
        }

        r = smb_session_setup(soc:soc, login:login, password:pass,
                              domain:domain, prot:prot);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        uid = session_extract_uid(reply:r);
        if(!uid)
        {
                close(soc);
                return FALSE;
        }

        r = smb_tconx(soc:soc, name:name, uid:uid, share:"IPC$");
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        tid = tconx_extract_tid(reply:r);
        if(!tid)
        {
                close(soc);
                return FALSE;
        }

        r = smbntcreatex(soc:soc, uid:uid, tid:tid, name:"\winreg");
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        pipe = smbntcreatex_extract_pipe(reply:r);
        if(!pipe)
        {
                close(soc);
                return FALSE;
        }

        r = pipe_accessible_registry(soc:soc, uid:uid, tid:tid, pipe:pipe);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        handle = registry_open_hklm(soc:soc, uid:uid, tid:tid, pipe:pipe);
        if(!handle)
        {
                close(soc);
                return FALSE;
        }

        r = registry_get_key(soc:soc, uid:uid, tid:tid, pipe:pipe, key:key,
                             reply:handle);

        if(!r)
        {
                close(soc);
                return FALSE;
        }

        list = registry_enum_value(soc:soc, uid:uid, tid:tid, pipe:pipe, reply:r);
        close(soc);
        return(list);
}


##############################################################################
# Enumerate registry keys
# param:
#	key: REGISTRY_KEY
#
###############################################################################

function registry_enum_keys(key)
{
        if(!port){
                port = 445;
        }

        soc = open_sock_tcp(port);
        if(!soc) {
                return FALSE;
        }

        r = smb_session_request(soc:soc, remote:name);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        prot = smb_neg_prot(soc:soc);
        if(!prot)
        {
                close(soc);
                 return FALSE;
        }

        r = smb_session_setup(soc:soc, login:login, password:pass,
                                 domain:domain, prot:prot);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        uid = session_extract_uid(reply:r);
        if(!uid)
        {
                close(soc);
                return FALSE;
        }

        r = smb_tconx(soc:soc, name:name, uid:uid, share:"IPC$");
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        tid = tconx_extract_tid(reply:r);
        if(!tid)
        {
                close(soc);
                 return FALSE;
        }

        r = smbntcreatex(soc:soc, uid:uid, tid:tid, name:"\winreg");
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        pipe = smbntcreatex_extract_pipe(reply:r);
        if(!pipe)
        {
                close(soc);
                return FALSE;
        }

        r = pipe_accessible_registry(soc:soc, uid:uid, tid:tid, pipe:pipe);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        handle = registry_open_hklm(soc:soc, uid:uid, tid:tid, pipe:pipe);
        if(!handle)
        {
                close(soc);
                return FALSE;
        }

        r = registry_get_key(soc:soc, uid:uid, tid:tid, pipe:pipe, key:key,
                             reply:handle);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        list = registry_enum_key(soc:soc, uid:uid, tid:tid, pipe:pipe, reply:r);
        close(soc);
        return(list);
}


################################################################################
#Open the given file
#param:
#	share: IPC Share, ex: $C
#	file: File path
################################################################################


function open_file(share, file)
{
        if(!port){
                port = 445;
        }

        soc = open_sock_tcp(port);
        if(!soc){
                return FALSE;
        }

        r = smb_session_request(soc:soc, remote:name);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        prot = smb_neg_prot(soc:soc);
        if(!prot)
        {
                close(soc);
                return FALSE;
        }

        r = smb_session_setup(soc:soc, login:login, password:pass,
                              domain:domain, prot:prot);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        uid = session_extract_uid(reply:r);
        if(!uid)
        {
                close(soc);
                return FALSE;
        }

        r = smb_tconx(soc:soc, name:name, uid:uid, share:share);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        tid = tconx_extract_tid(reply:r);
        if(!tid)
        {
                close(soc);
                return FALSE;
        }

        r = OpenAndX(socket:soc, uid:uid, tid:tid, file:file);
        if(r)
          smb_close_request(soc:soc, uid:uid, tid:tid, fid:r);

        close(soc);
        return r;
}


##########################################################################
# Get the file size for a given file
# Param
#       share: Windows IPC Share
#       file: Path to a given file
##########################################################################

function get_file_size(share, file)
{
        if(!port){
                port = 445;
        }

        soc = open_sock_tcp(port);
        if(!soc){
                return FALSE;
        }

        r = smb_session_request(soc:soc, remote:name);
        if(!r){
                close(soc);
                return FALSE;
        }

        prot = smb_neg_prot(soc:soc);
        if(!prot){
                close(soc);
                return FALSE;
        }

        r = smb_session_setup(soc:soc, login:login, password:pass,
                              domain:domain, prot:prot);
        if(!r){
                close(soc);
                return FALSE;
        }

        uid = session_extract_uid(reply:r);
        if(!uid)
        {
                close(soc);
                return FALSE;
        }

        r = smb_tconx(soc:soc, name:name, uid:uid, share:share);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        tid = tconx_extract_tid(reply:r);
        if(!tid){
                close(soc);
                return FALSE;
        }

        fid = OpenAndX(socket:soc, uid:uid, tid:tid, file:file);
        if(!fid){
                close(soc);
                return FALSE;
        }
        fileSize = smb_get_file_size(socket:soc, uid:uid, tid:tid, fid:fid);
        smb_close_request(soc:soc, uid:uid, tid:tid, fid:fid);
        close(soc);

        if(fileSize){
                return fileSize;
        }
}




##########################################################################
# Function that reads the content of given file
# Param
#	share: Windows IPC Share
#	file: File path to read the content
#	offset: Position from which to start the file read
#	count: Number of bytes to readh from offset
##########################################################################

function read_file(share, file, offset, count)
{
        if(!port){
                port = 445;
        }

        soc = open_sock_tcp(port);
        if(!soc){
                return FALSE;
        }

        r = smb_session_request(soc:soc, remote:name);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        prot = smb_neg_prot(soc:soc);
        if(!prot)
        {
                close(soc);
                return FALSE;
        }

        r = smb_session_setup(soc:soc, login:login, password:pass,
                                 domain:domain, prot:prot);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        uid = session_extract_uid(reply:r);
        if(!uid)
        {
                close(soc);
                return FALSE;
        }

        r = smb_tconx(soc:soc, name:name, uid:uid, share:share);
        if(!r)
        {
                close(soc);
                return FALSE;
        }

        tid = tconx_extract_tid(reply:r);
        if(!tid)
        {
                close(soc);
                return FALSE;
        }

        fid = OpenAndX(socket:soc, uid:uid, tid:tid, file:file);
        if(!fid)
        {
                close(soc);
                return FALSE;
        }
        content = ReadAndX(socket:soc, uid:uid, tid:tid, fid:fid,
                           count:count, off:offset);
        result = smb_close_request(soc:soc, uid:uid, tid:tid, fid:fid);
        close(soc);
        return content;
}

function GetVer(file, share, prodvers)
{
  local_var pe_offset, sections_cnt, sections_offset, sections_data, i, rsrc_start, rsrc_virtstart;
  local_var dir_data, dir_offset, dir_entries, sub_dir, dir_tmp, vs_data, vs_length, reply, recurs, max_recurs, vs_offset;
  local_var itr, it;

  if(isnull(prodvers)){
    prodvers = 0;
  }

  pe_offset = bin_word(data: read_file(share:share, file:file, offset:60, count:2));

  if(read_file(share:share, file:file, offset:pe_offset, count:2) != "PE"){
    return NULL;
  }

  sections_cnt = bin_word(data: read_file(share:share, file:file,
                          offset:pe_offset+6, count:2));
  section_offset = pe_offset + bin_word(data:read_file(share:share, file:file,
                                        offset:pe_offset+20, count:2)) + 24;
  rsrc_start = NULL;
  max_recurs = 20;

  for( itr=0; itr<sections_cnt; itr++ )
  {
    sections_data = read_file(share:share, file:file, offset:section_offset + itr * 40,
                              count:40);
    if(substr(sections_data, 0, 4) == ".rsrc")
    {
      rsrc_start = bin_dword(data:substr(sections_data, 20));
      rsrc_virtstart = bin_dword(data:substr(sections_data, 12));
      break;
    }
    if(itr > max_recurs){
      break;
    }
  }
  if(isnull(rsrc_start)){
    return NULL;
  }

  dir_data = read_file(share:share, file:file, offset:rsrc_start, count:16);
  dir_entries = bin_word(data:substr(dir_data,12)) + bin_word(data:substr(dir_data,14));
  dir_offset = NULL;
  sub_dir = 0;
  max_recurs = 100;
  recurs = 0;
  for(it = 0; it < dir_entries; it++)
  {
    dir_data = read_file(file:file, offset:rsrc_start+16+it*8, count:8);
    if(bin_dword(data:substr(dir_data,0)) == 0x10)
    {
      repeat
      {
        dir_tmp = bin_dword(data:substr(dir_data,4));
        if((dir_tmp & 0x80000000) == 0x80000000)
        {
          dir_offset = dir_tmp - 0x80000000;
          sub_dir = 1;
        }
        else
        {
          dir_offset = dir_tmp;
          sub_dir = 0;
        }
        dir_data = read_file(share:share, file:file, offset:rsrc_start+dir_offset+16,
                             count:8);
        if(strlen(dir_data) != 8){
          return NULL;
        }
        if(++recurs > max_recurs){
          return NULL;
        }
      }
      until sub_dir == 0;
      break;
    }
    if(it > max_recurs){
      break;
    }
  }
  if(isnull(dir_offset)){
    return NULL;
  }

  dir_data = read_file(share:share, file:file, offset:rsrc_start+dir_offset,
                       count:4);
  dir_offset = bin_dword(data:substr(dir_data,0));
  rsrc_start = rsrc_start + (dir_offset - rsrc_virtstart);
  vs_data = read_file(share:share, file:file, offset:rsrc_start, count:2);
  if(!vs_data){
    return NULL;
  }

  vs_length = bin_word(data:substr(vs_data,0));
  vs_data = read_file(share:share, file:file, offset:rsrc_start, count:vs_length);

  if(bin_dword(data:substr(vs_data,40)) != 0xfeef04bd){
    return NULL;
  }

  if(prodvers == 0){
    vs_offset = 48;
  }
  else{
    vs_offset = 56;
  }

  reply = NULL;
  reply = string((bin_dword(data:substr(vs_data, vs_offset)) >>> 16) + ".");
  reply = reply + string((bin_dword(data:substr(vs_data, vs_offset)) & 0xffff) + ".");
  reply = reply + string((bin_dword(data:substr(vs_data, vs_offset+4)) >>> 16) + ".");
  reply = reply + string((bin_dword(data:substr(vs_data, vs_offset+4)) & 0xffff));
  return reply;
}

function bin_word(data)
{
  if(!data){
    return 0;
  }
  else return(ord(data[0]) + (ord(data[1]) << 8));
}


function bin_dword(data)
{
  if(!data){
    return 0;
  }
  else {
    return(ord(data[0]) + (ord(data[1]) << 8) +
         (ord(data[2]) << 16) + (ord(data[3]) << 24));
  }
}

function GetVersionFromFile(file, verstr) {

  local_var file, share, verstr, mshare, soc, r, prot, uid, tid, ver;

  mshare = ereg_replace(pattern:"([A-Z]):.*", replace:"\1$", string:file);
  file = ereg_replace(pattern:"[A-Z]:(.*)", replace:"\1", string:file);

  soc = open_sock_tcp(port);
  if(!soc){
    return NULL;
  }

  r = smb_session_request(soc:soc, remote:name);
  if(!r)
  {
    close(soc);
    return NULL;
  }

  prot = smb_neg_prot(soc:soc);
  if(!prot)
  {
    close(soc);
    return NULL;
  }

  r = smb_session_setup(soc:soc, login:login, password:pass,
                        domain:domain, prot:prot);
  if(!r)
  {
    close(soc);
    return NULL;
  }

  uid = session_extract_uid(reply:r);
  r = smb_tconx(soc:soc, name:name, uid:uid, share:mshare);

  tid = tconx_extract_tid(reply:r);
  if(!tid)
  {
    close(soc);
    return NULL;
  }
  fid = OpenAndX(socket:soc, uid:uid, tid:tid, file:file);
  if(!fid)
  {
    close(soc);
    return NULL;
  }
  if(isnull(verstr)) {
    ver = GetVersion(socket:soc, uid:uid, tid:tid, fid:fid);
  } else {
    ver = GetVersion(socket:soc, uid:uid, tid:tid, fid:fid,verstr:verstr);
  }
  close(soc);

  return ver;


}
