2010-01-25 15:58:24 +00:00
require 'rex/proto/drda'
module Rex
module Proto
module DRDA
class Error < StandardError ; end
class RespError < Error ; end
# See:
# http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.drda/db2z_excsat.htm
class MGRLVLLS_PARAM < Struct . new ( :length , :codepoint , :payload )
def initialize ( args = { } )
self [ :codepoint ] = Constants :: MGRLVLLS
self [ :payload ] = " \x14 \x03 \x00 \x0a \x24 \x07 \x00 \x0a " +
" \x14 \x74 \x00 \x05 \x24 \x0f \x00 \x08 " +
" \x14 \x40 \x00 \x09 \x1c \x08 \x04 \xb8 "
self [ :length ] = self [ :payload ] . to_s . size + 4
end
def to_s
self . to_a . pack ( " nna* " )
end
end
# Currently, only takes a MGRLVLLS param. Extend the struct
# when more parameters are defined.
class EXCSAT_DDM < Struct . new ( :length , :magic , :format , :correlid , :length2 ,
2010-01-28 23:08:39 +00:00
:codepoint , :mgrlvlls )
2010-01-25 15:58:24 +00:00
def initialize ( args = { } )
self [ :magic ] = 0xd0
self [ :format ] = 0x41
self [ :correlid ] = 1
self [ :codepoint ] = Constants :: EXCSAT
self [ :mgrlvlls ] = args [ :mgrlvlls ] || MGRLVLLS_PARAM . new . to_s
self [ :length ] = ( 10 + self [ :mgrlvlls ] . to_s . size )
self [ :length2 ] = self [ :length ] - 6
end
def to_s
packstr = " nCCnnn "
packstr += " a* " # Pack smarter as more params are added.
self . to_a . pack ( packstr )
end
end
# See http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.drda/db2z_accsec.htm
# for all sorts of info about SECMEC.
class SECMEC_PARAM < Struct . new ( :length , :codepoint , :payload )
def initialize ( args = { } )
self [ :length ] = 6
self [ :codepoint ] = Constants :: SECMEC
self [ :payload ] = 3 # Plaintext username and password.
end
def to_s
self . to_a . pack ( " nnn " )
end
end
# Relational Database name parameter.
class RDBNAM_PARAM < Struct . new ( :length , :codepoint , :payload )
def initialize ( args = { } )
self [ :length ] = 22 # Since the database name is padded out.
self [ :codepoint ] = Constants :: RDBNAM
self [ :payload ] = encode ( args [ :payload ] . to_s )
end
def encode ( str )
Rex :: Text . to_ebcdic ( [ str ] . pack ( " A18 " ) )
end
def payload = ( str )
self [ :payload ] = encode ( str . to_s )
end
def to_s
self . to_a . pack ( " nna18 " )
end
end
# The ACCSEC DDM is responsible for picking the security mechanism (SECMEC)
# which, in our case, will always be plain text username and password. It
# also sets the relational database name (RDBNAM), if specified. You need
# one to login, but not to probe.
class ACCSEC_DDM < Struct . new ( :length , :magic , :format , :correlid , :length2 ,
2010-01-28 23:08:39 +00:00
:codepoint , :secmec , :rdbnam )
2010-01-25 15:58:24 +00:00
def initialize ( args = { } )
self [ :magic ] = 0xd0
self [ :format ] = args [ :format ] || 0x01
self [ :correlid ] = 2
self [ :codepoint ] = Constants :: ACCSEC
self [ :secmec ] = SECMEC_PARAM . new . to_s
if args [ :dbname ] # Include a database name if we're given one.
self [ :rdbnam ] = RDBNAM_PARAM . new ( :payload = > args [ :dbname ] ) . to_s
end
self [ :length ] = 10 + self [ :secmec ] . to_s . size + self [ :rdbnam ] . to_s . size
self [ :length2 ] = self [ :length ] - 6
end
def dbname = ( str )
self [ :rdbnam ] = RDBNAM_PARAM . new ( :payload = > args [ :dbname ] ) . to_s
end
def to_s
packstr = " nCCnnna6 "
packstr += " a22 " if self [ :rdbnam ]
self . to_a . pack ( packstr )
end
end
class DDM_PARAM < Struct . new ( :length , :codepoint , :payload )
def read ( str = " " )
raise DRDA :: Error , " Input isn't a String. " if ! str . kind_of? String
raise DRDA :: RespError , " DDM_PARAM is too short " if str . size < 4
( self [ :length ] , self [ :codepoint ] ) =
str . unpack ( " nn " )
raise DRDA :: RespError , " DDM_PARAM Length is too short " if self [ :length ] < 4
rest = str [ 4 , self [ :length ] - 4 ] # If it's negative or whatever, it'll end up as "".
self [ :payload ] = rest . to_s [ 0 , self [ :length ] - 4 ]
return self
end
def to_s
self . to_a . pack ( " nna* " )
end
end
class BASIC_DDM < Struct . new ( :length , :magic , :format , :correlid ,
2010-01-28 23:08:39 +00:00
:length2 , :codepoint , :payload )
2010-01-25 15:58:24 +00:00
def initialize
self [ :payload ] = [ ]
end
def read ( str = " " )
self [ :payload ] . clear
raise DRDA :: Error , " Input isn't a String. " if ! str . kind_of? String
raise DRDA :: RespError , " Response is too short. " if str . size < 10
( self [ :length ] , self [ :magic ] , self [ :format ] ,
self [ :correlid ] , self [ :length2 ] , self [ :codepoint ] ) =
str . unpack ( " nCCnnn " )
sanity_check
rest = str [ 10 , self [ :length2 ] - 4 ]
i = 0
while ( i < rest . size )
if self [ :codepoint ] == Constants :: SQLCARD # These aren't DDM's.
this_param = rest [ i , self [ :length ] - 10 ]
else
this_param = DDM_PARAM . new . read ( rest [ i , rest . size ] )
end
self [ :payload ] << this_param
i += this_param . to_s . size
end
return self
end
# Just a quick test.
def sanity_check
if self [ :length ] < 10
raise DRDA :: RespError , " DDM Length is too short. "
elsif self [ :length2 ] < 4
raise DRDA :: RespError , " DDM Length2 is too short. "
elsif self [ :length ] - 6 != self [ :length2 ]
2010-01-28 23:01:32 +00:00
raise DRDA :: RespError , " Codepoint: 0x #{ self [ :codepoint ] . to_s ( 16 ) } DDM Length2 (0x #{ self [ :length2 ] . to_s ( 16 ) } ) isn't six less than Length (0x #{ self [ :length ] . to_s ( 16 ) } ) "
2010-01-25 15:58:24 +00:00
end
end
def to_s
self . to_a . pack ( " nCCnnn " ) + self [ :payload ] . map { | x | x . to_s } . join
end
end
class SERVER_PACKET < Array
def read ( str = " " )
raise DRDA :: Error , " Input isn't a String. " if ! str . kind_of? String
self . clear
i = 0
while ( i < str . size )
this_ddm = BASIC_DDM . new . read ( str [ i , str . size ] )
self << this_ddm
i += this_ddm . to_s . size
end
return self
end
def to_s ; self . join ; end
def sz ; self . to_s . size ; end
end
class PASSWORD_PARAM < Struct . new ( :length , :codepoint , :payload )
def initialize ( args = { } )
self [ :codepoint ] = Constants :: PASSWORD
self [ :payload ] = Rex :: Text . to_ebcdic ( args [ :payload ] . to_s )
self [ :length ] = self [ :payload ] . size + 4
end
def encode ( str )
Rex :: Text . to_ebcdic ( str )
end
def to_s
self . to_a . pack ( " nna* " )
end
end
class USERID_PARAM < Struct . new ( :length , :codepoint , :payload )
def initialize ( args = { } )
self [ :codepoint ] = Constants :: USERID
self [ :payload ] = Rex :: Text . to_ebcdic ( args [ :payload ] . to_s )
self [ :length ] = self [ :payload ] . size + 4
end
def encode ( str )
Rex :: Text . to_ebcdic ( str )
end
def to_s
self . to_a . pack ( " nna* " )
end
end
class SECCHK_DDM < Struct . new ( :length , :magic , :format , :correlid , :length2 ,
2010-01-28 23:08:39 +00:00
:codepoint , :secmec , :rdbnam , :password , :userid )
2010-01-25 15:58:24 +00:00
def initialize ( args = { } ) # Takes :dbname, :dbpass, :dbuser
self [ :magic ] = 0xd0
self [ :format ] = 0x01
self [ :correlid ] = 2
self [ :codepoint ] = Constants :: SECCHK
self [ :secmec ] = SECMEC_PARAM . new . to_s
if args [ :dbname ] # Include a database name if we're given one.
self [ :rdbnam ] = RDBNAM_PARAM . new ( :payload = > args [ :dbname ] ) . to_s
end
self [ :password ] = PASSWORD_PARAM . new ( :payload = > args [ :dbpass ] ) . to_s
self [ :userid ] = USERID_PARAM . new ( :payload = > args [ :dbuser ] ) . to_s
self [ :length ] = ( 10 + self [ :secmec ] . to_s . size + self [ :rdbnam ] . to_s . size +
self [ :password ] . to_s . size + self [ :userid ] . to_s . size )
self [ :length2 ] = self [ :length ] - 6
end
def dbname = ( str )
self [ :rdbnam ] = RDBNAM_PARAM . new ( :payload = > args [ :dbname ] ) . to_s
end
def to_s
packstr = " nCCnnna6 "
packstr += " a22 " if self [ :rdbnam ]
packstr += " a*a* " # username and password
self . to_a . pack ( packstr )
end
end
end
end
end