Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
30
third_party/FreeRDP/tools/build-clang.sh
vendored
Executable file
30
third_party/FreeRDP/tools/build-clang.sh
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash -xe
|
||||
SCRIPT_PATH=$(dirname "${BASH_SOURCE[0]}")
|
||||
SCRIPT_PATH=$(realpath "$SCRIPT_PATH")
|
||||
|
||||
WARN_FLAGS="-Weverything -Wno-padded -Wno-assign-enum -Wno-switch-enum \
|
||||
-Wno-declaration-after-statement -Wno-c++98-compat -Wno-c++98-compat-pedantic \
|
||||
-Wno-cast-align -Wno-covered-switch-default -Wno-documentation-unknown-command \
|
||||
-Wno-documentation -Wno-documentation-html"
|
||||
|
||||
cmake \
|
||||
-GNinja \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$SCRIPT_PATH/../cmake/ClangToolchain.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_FIND_LIBRARY_SUFFIXES=".a;.so" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-Bclang \
|
||||
-S"$SCRIPT_PATH/.." \
|
||||
-DCMAKE_INSTALL_PREFIX=/tmp/xxx \
|
||||
-DWITH_SERVER=ON \
|
||||
-DWITH_SAMPLE=ON \
|
||||
-DWITH_CAIRO=ON \
|
||||
-DWITH_FFMPEG=ON \
|
||||
-DWITH_DSP_FFMPEG=ON \
|
||||
-DWITH_PKCS11=ON \
|
||||
-DWITH_SOZR=ON \
|
||||
-DWITH_WAYLAND=ON \
|
||||
-DWITH_WEBVIEW=ON \
|
||||
-DWITH_SWSCALE=ON \
|
||||
-DCMAKE_C_FLAGS="$WARN_FLAGS" \
|
||||
-DCMAKE_CXX_FLAGS="$WARN_FLAGS"
|
||||
726
third_party/FreeRDP/tools/smartcard-interpreter.py
vendored
Normal file
726
third_party/FreeRDP/tools/smartcard-interpreter.py
vendored
Normal file
@@ -0,0 +1,726 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright 2022 David Fort <contact@hardening-consulting.com>
|
||||
#
|
||||
# This script is meant to parse some FreeRDP logs in DEBUG mode (WLOG_LEVEL=DEBUG) and interpret the
|
||||
# smartcard traffic, dissecting the PIV or GIDS commands
|
||||
#
|
||||
# usage:
|
||||
# * live: WLOG_LEVEL=DEBUG xfreerdp <args with smartcard> | python3 smartcard-interpreter.py
|
||||
# * on an existing log file: python3 smartcard-interpreter.py <log file>
|
||||
#
|
||||
import sys
|
||||
import codecs
|
||||
|
||||
|
||||
CMD_NAMES = {
|
||||
0x04: "DEACTIVATE FILE",
|
||||
0x0C: "ERASE RECORD",
|
||||
0x0E: "ERASE BINARY",
|
||||
0x0F: "ERASE BINARY",
|
||||
0x20: "VERIFY",
|
||||
0x21: "VERIFY",
|
||||
0x22: "MSE",
|
||||
0x24: "CHANGE REFERENCE DATA",
|
||||
0x25: "MSE",
|
||||
0x26: "DISABLE VERIFICATION REQUIREMENT",
|
||||
0x28: "ENABLE VERIFICATION REQUIREMENT",
|
||||
0x2A: "PSO",
|
||||
0x2C: "RESET RETRY COUNTER",
|
||||
0x2D: "RESET RETRY COUNTER",
|
||||
0x44: "ACTIVATE FILE",
|
||||
0x46: "GENERATE ASYMMETRIC KEY PAIR",
|
||||
0x47: "GENERATE ASYMMETRIC KEY PAIR",
|
||||
0x84: "GET CHALLENGE",
|
||||
0x86: "GENERAL AUTHENTICATE",
|
||||
0x87: "GENERAL AUTHENTICATE",
|
||||
0x88: "INTERNAL AUTHENTICATE",
|
||||
0xA0: "SEARCH BINARY",
|
||||
0xA1: "SEARCH BINARY",
|
||||
0xA2: "SEARCH RECORD",
|
||||
0xA4: "SELECT",
|
||||
0xB0: "READ BINARY",
|
||||
0xB1: "READ BINARY",
|
||||
0xB2: "READ RECORD",
|
||||
0xB3: "READ RECORD",
|
||||
0xC0: "GET RESPONSE",
|
||||
0xC2: "ENVELOPE",
|
||||
0xC3: "ENVELOPE",
|
||||
0xCA: "GET DATA",
|
||||
0xCB: "GET DATA",
|
||||
0xD0: "WRITE BINARY",
|
||||
0xD1: "WRITE BINARY",
|
||||
0xD2: "WRITE RECORD",
|
||||
0xD6: "UPDATE BINARY",
|
||||
0xD7: "UPDATE BINARY",
|
||||
0xDA: "PUT DATA",
|
||||
0xDB: "PUT DATA",
|
||||
0xDC: "UPDATE RECORD",
|
||||
0xDD: "UPDATE RECORD",
|
||||
0xE0: "CREATE FILE",
|
||||
0xE2: "APPEND RECORD",
|
||||
0xE4: "DELETE FILE",
|
||||
0xE6: "TERMINATE DF",
|
||||
0xE8: "TERMINATE EF",
|
||||
0xFE: "TERMINATE CARD USAGE",
|
||||
}
|
||||
|
||||
AIDs = {
|
||||
"a00000039742544659": "MsGidsAID",
|
||||
"a000000308": "PIV",
|
||||
"a0000003974349445f0100": "SC PNP",
|
||||
"a0000001510000": "GPC",
|
||||
}
|
||||
|
||||
FIDs = {
|
||||
0x0000: "Current EF",
|
||||
0x2F00: "EF.DIR",
|
||||
0x2F01: "EF.ATR",
|
||||
0x3FFF: "Current application(ADF)",
|
||||
}
|
||||
|
||||
DOs = {
|
||||
"df1f": "DO_FILESYSTEMTABLE",
|
||||
"df20": "DO_CARDID",
|
||||
"df21": "DO_CARDAPPS",
|
||||
"df22": "DO_CARDCF",
|
||||
"df23": "DO_CMAPFILE",
|
||||
"df24": "DO_KXC00",
|
||||
}
|
||||
|
||||
ERROR_CODES = {
|
||||
0x9000: "success",
|
||||
0x6100: "more bytes",
|
||||
0x6282: "end of file or record",
|
||||
0x6283: "card locked",
|
||||
0x63C0: "warning counter 0",
|
||||
0x63C1: "warning counter 1",
|
||||
0x63C2: "warning counter 2",
|
||||
0x63C3: "warning counter 3",
|
||||
0x63C4: "warning counter 4",
|
||||
0x63C5: "warning counter 5",
|
||||
0x63C6: "warning counter 6",
|
||||
0x63C7: "warning counter 7",
|
||||
0x63C8: "warning counter 8",
|
||||
0x63C9: "warning counter 9",
|
||||
0x6982: "security status not satisfied",
|
||||
0x6882: "Secure messaging not supported",
|
||||
0x6985: "condition of use not satisfied",
|
||||
0x6A80: "incorrect parameter cmd data field",
|
||||
0x6A81: "function not supported",
|
||||
0x6A82: "file or application not found",
|
||||
0x6A83: "record not found",
|
||||
0x6A88: "REFERENCE DATA NOT FOUND",
|
||||
0x6D00: "unsupported",
|
||||
}
|
||||
|
||||
PIV_OIDs = {
|
||||
"5fc101": "X.509 Certificate for Card Authentication",
|
||||
"5fc102": "Card Holder Unique Identifier",
|
||||
"5fc103": "Cardholder Fingerprints",
|
||||
"5fc105": "X.509 Certificate for PIV Authentication",
|
||||
"5fc106": "Security Object",
|
||||
"5fc107": "Card Capability Container",
|
||||
"5fc108": "Cardholder Facial Image",
|
||||
"5fc10a": "X.509 Certificate for Digital Signature",
|
||||
"5fc10b": "X.509 Certificate for Key Management",
|
||||
"5fc10d": "Retired X.509 Certificate for Key Management 1",
|
||||
"5fc10e": "Retired X.509 Certificate for Key Management 2",
|
||||
"5fc10f": "Retired X.509 Certificate for Key Management 3",
|
||||
}
|
||||
|
||||
class ApplicationDummy(object):
|
||||
"""Base application"""
|
||||
|
||||
def __init__(self, aid):
|
||||
self.aid = aid
|
||||
|
||||
def getAID(self):
|
||||
return self.aid
|
||||
|
||||
def selectResult(self, fci, status, body):
|
||||
return 'selectResult(fci=%s, status=0x%x) = %s\n' %(fci, status, body.hex())
|
||||
|
||||
def getData(self, fileId, bytes):
|
||||
return 'getData(status=0x%x) = %s\n' %(fileId, bytes.hex())
|
||||
|
||||
def getDataResult(self, status, body):
|
||||
return 'getDataResult(status=0x%x) = %s\n' %(status, body.hex())
|
||||
|
||||
def mse(self, body):
|
||||
return body.hex()
|
||||
|
||||
def mseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def pso(self, body):
|
||||
return body.hex()
|
||||
|
||||
def psoResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponse(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def verify(self, status, body):
|
||||
return "verify(%s)" % body.hex()
|
||||
|
||||
def verifyResult(self, status, body):
|
||||
return "verify(%s)" % body.hex()
|
||||
|
||||
|
||||
'''
|
||||
'''
|
||||
class ApplicationGpc(object):
|
||||
"""GlobalPlatform application"""
|
||||
|
||||
def __init__(self, aid):
|
||||
self.aid = aid
|
||||
self.lastGetData = None
|
||||
|
||||
def getAID(self):
|
||||
return self.aid
|
||||
|
||||
def selectResult(self, fci, status, body):
|
||||
return 'selectResult(fci=%s, status=0x%x) = %s\n' %(fci, status, body.hex())
|
||||
|
||||
def getData(self, fileId, bytes):
|
||||
tags = {
|
||||
0x42: 'Issuer Identification Number',
|
||||
0x45: 'Card Image Number',
|
||||
0x66: 'Card Data',
|
||||
0x67: 'Card Capability Information' # ???
|
||||
}
|
||||
self.lastGetData = fileId
|
||||
return 'getData(%s)\n' % tags.get(fileId, '<unknown 0x%x>' % fileId)
|
||||
|
||||
def getDataResult(self, status, body):
|
||||
if self.lastGetData == 0x66:
|
||||
# Card Data
|
||||
pass
|
||||
return 'getDataResult(0x%x) = %s\n' %(status, body.hex())
|
||||
|
||||
def mse(self, body):
|
||||
return body.hex()
|
||||
|
||||
def mseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def pso(self, body):
|
||||
return body.hex()
|
||||
|
||||
def psoResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponse(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
|
||||
class ApplicationPIV(object):
|
||||
"""PIV application"""
|
||||
|
||||
def __init__(self, aid):
|
||||
self.lastGet = None
|
||||
self.aid = aid
|
||||
|
||||
def getAID(self):
|
||||
return self.aid
|
||||
|
||||
def selectResult(self, selectT, status, body):
|
||||
ret = ''
|
||||
appTag = body[0]
|
||||
appLen = body[1]
|
||||
|
||||
body = body[2:2+appLen]
|
||||
while len(body) > 2:
|
||||
tag = body[0]
|
||||
tagLen = body[1]
|
||||
if tagLen != 1:
|
||||
pass
|
||||
if selectT == "FCI":
|
||||
if tag == 0x4f:
|
||||
ret += "\tpiv version: %s\n" % body[2:2 + tagLen].hex()
|
||||
elif tag == 0x79:
|
||||
subBody = body[2:2 + tagLen]
|
||||
|
||||
subTag = subBody[0]
|
||||
subLen = subBody[1]
|
||||
|
||||
content = subBody.hex()
|
||||
if subTag == 0x4f:
|
||||
v = content[4:]
|
||||
if v.startswith('a000000308'):
|
||||
content = 'NIST RID'
|
||||
ret += '\tCoexistent tag allocation authority: %s\n' % content
|
||||
|
||||
elif tag == 0x50:
|
||||
ret += '\tapplication label: %s\n' % body[2:2+tagLen].decode('utf8')
|
||||
elif tag == 0xac:
|
||||
ret += '\tCryptographic algorithms supported: %s\n' % body[2:2+tagLen].hex()
|
||||
else:
|
||||
rety += '\tunknown tag 0x%x\n' % tag
|
||||
|
||||
else:
|
||||
ret += "\tTODO: selectType %s\n" % selectT
|
||||
|
||||
body = body[2+tagLen:]
|
||||
|
||||
return ret
|
||||
|
||||
def getData(self, fileId, bytes):
|
||||
ret = "\tfileId=%s(%0.4x)\n" % (FIDs.get(fileId, ""), fileId)
|
||||
|
||||
if len(bytes) < 7:
|
||||
return ret + "\t/!\\ too short !!!!"
|
||||
|
||||
lc = bytes[4]
|
||||
tag = bytes[5]
|
||||
tagLen = bytes[6]
|
||||
|
||||
if lc == 4:
|
||||
ret += "\tdoId=%0.4x\n"% (bytes[7] * 256 + bytes[8])
|
||||
|
||||
elif lc == 0xa:
|
||||
keyStr = ''
|
||||
# TLV
|
||||
i = 7
|
||||
tag = bytes[i]
|
||||
tagLen = bytes[i+1]
|
||||
keyRef = bytes[i+3]
|
||||
keyStr = "key(tag=0x%x len=%d ref=0x%x)=" % (tag, tagLen, keyRef)
|
||||
i = i + 2 + tagLen
|
||||
|
||||
tag = bytes[i]
|
||||
tagLen = bytes[i+1]
|
||||
keyStr += "value(tag=0x%x len=%d)"
|
||||
elif lc == 3:
|
||||
ret += "\tDiscovery Object\n"
|
||||
elif lc == 4:
|
||||
ret += "\tBiometric Information Templates (BIT) Group Template\n"
|
||||
elif lc == 5:
|
||||
if tag == 0x5C:
|
||||
tagStr = bytes[7:10].hex()
|
||||
ret += '\ttag: %s(%s)\n' % (tagStr, PIV_OIDs.get(tagStr, '<unknown>'))
|
||||
self.lastGet = tagStr
|
||||
else:
|
||||
ret += "\tunknown key access(lc=0x%x)\n" % lc
|
||||
|
||||
return ret
|
||||
|
||||
def getDataResult(self, status, body):
|
||||
ret = ''
|
||||
if not len(body):
|
||||
return ''
|
||||
appTag = body[0]
|
||||
appLen = body[1]
|
||||
|
||||
body = body[2:2+appLen]
|
||||
while len(body) > 2:
|
||||
tag = body[0]
|
||||
tagLen = body[1]
|
||||
tagBody = body[2:2+tagLen]
|
||||
|
||||
if self.lastGet in ('5fc102',):
|
||||
# Card holder Unique Identifier
|
||||
if tag == 0x30:
|
||||
ret += '\tFASC-N: %s\n' % tagBody.hex()
|
||||
elif tag == 0x34:
|
||||
ret += '\tGUID: %s\n' % tagBody.hex()
|
||||
elif tag == 0x35:
|
||||
ret += '\texpirationDate: %s\n' % tagBody.decode('utf8')
|
||||
elif tag == 0x3e:
|
||||
ret += '\tIssuer Asymmetric Signature: %s\n' % tagBody.hex()
|
||||
else:
|
||||
ret += "\tunknown tag=0x%x len=%d content=%s\n" % (tag, tagLen, tagBody.hex())
|
||||
|
||||
elif self.lastGet in ('5fc107',):
|
||||
# Card Capability Container
|
||||
capas = {
|
||||
0xf0: "Card Identifier",
|
||||
0xf1: "Capability Container version number",
|
||||
0xf2: "Capability Grammar version number",
|
||||
0xf3: "Applications CardURL",
|
||||
0xf4: "PKCS#15",
|
||||
0xf5: "Registered Data Model number",
|
||||
0xf6: "Access Control Rule Table",
|
||||
0xf7: "Card APDUs",
|
||||
0xfa: "Redirection Tag",
|
||||
0xfb: "Capability Tuples (CTs)",
|
||||
0xfc: "Status Tuples (STs)",
|
||||
0xfd: "Next CCC",
|
||||
0xe3: "Extended Application CardURL",
|
||||
0xb4: "Security Object Buffer",
|
||||
0xfe: "Error Detection Code"
|
||||
}
|
||||
|
||||
if tag in capas.keys():
|
||||
if tagLen:
|
||||
ret += "\t%s: len=%d %s\n" % (capas[tag], tagLen, tagBody.hex())
|
||||
else:
|
||||
ret += "\tunknown capa tag 0x%x: %s\n" % (tag, tagBody.hex())
|
||||
|
||||
elif self.lastGet == '5fc105':
|
||||
# X.509 Certificate for PIV Authentication
|
||||
pass
|
||||
|
||||
else:
|
||||
ret += "\t%s: unimplemented tag=0x%x len=%d content=%s\n" % (self.lastGet, tag, tagLen, tagBody.hex())
|
||||
|
||||
body = body[2+tagLen:]
|
||||
|
||||
return ret
|
||||
|
||||
def getResponse(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def mse(self, body):
|
||||
return body.hex()
|
||||
|
||||
def mseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def pso(self, body):
|
||||
return body.hex()
|
||||
|
||||
def psoResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def verify(self, status, body):
|
||||
return "verify(%s)" % body.hex()
|
||||
|
||||
def verifyResult(self, status, body):
|
||||
return "verify(%s)" % body.hex()
|
||||
|
||||
|
||||
|
||||
|
||||
class ApplicationGids(object):
|
||||
"""GIDS application"""
|
||||
|
||||
def __init__(self, aid):
|
||||
self.aid = aid
|
||||
self.lastDo = None
|
||||
|
||||
def getAID(self):
|
||||
return self.aid
|
||||
|
||||
def parseFcp(self, bytes):
|
||||
ret = ''
|
||||
tag = bytes[0]
|
||||
tagLen = bytes[1]
|
||||
|
||||
body = bytes[2:2+tagLen]
|
||||
|
||||
if tag == 0x62:
|
||||
ret += '\tFCP\n'
|
||||
|
||||
while len(body) > 2:
|
||||
tag2 = body[0]
|
||||
tag2Len = body[1]
|
||||
tag2Body = body[2:2+tag2Len]
|
||||
|
||||
if tag2 == 0x82:
|
||||
ret += '\t\tFileDescriptor: %s\n' % tag2Body.hex()
|
||||
elif tag2 == 0x8a:
|
||||
ret += '\t\tLifeCycleByte: %s\n' % tag2Body.hex()
|
||||
elif tag2 == 0x84:
|
||||
ret += '\t\tDF name: %s\n' % tag2Body.encode('utf8')
|
||||
elif tag2 == 0x8C:
|
||||
ret += '\t\tSecurityAttributes: %s\n' % tag2Body.hex()
|
||||
else:
|
||||
ret += '\t\tunhandled tag=0x%x body=%s\n' % (tag2, tag2Body.hex())
|
||||
|
||||
body = body[2+tag2Len:]
|
||||
|
||||
return ret
|
||||
|
||||
def parseFci(self, bytes):
|
||||
ret = ''
|
||||
tag = bytes[0]
|
||||
tagLen = bytes[1]
|
||||
|
||||
body = bytes[2:2+tagLen]
|
||||
|
||||
if tag == 0x61:
|
||||
ret += '\tFCI\n'
|
||||
|
||||
while len(body) > 2:
|
||||
tag2 = body[0]
|
||||
tag2Len = body[1]
|
||||
tag2Body = body[2:2+tag2Len]
|
||||
|
||||
if tag2 == 0x4F:
|
||||
ret += '\t\tApplication AID: %s\n' % tag2Body.hex()
|
||||
|
||||
elif tag2 == 0x50:
|
||||
ret += '\t\tApplication label: %s\n' % tag2Body.encode('utf8')
|
||||
|
||||
elif tag2 == 0x73:
|
||||
body2 = tag2Body
|
||||
tokens = []
|
||||
while len(body2) > 2:
|
||||
tag3 = body2[0]
|
||||
tag3Len = body2[1]
|
||||
|
||||
if tag3 == 0x40:
|
||||
v = body2[2]
|
||||
if v & 0x80:
|
||||
tokens.append('mutualAuthSymAlgo')
|
||||
if v & 0x40:
|
||||
tokens.append('extAuthSymAlgo')
|
||||
if v & 0x20:
|
||||
tokens.append('keyEstabIntAuthECC')
|
||||
|
||||
|
||||
body2 = body2[2+tag3Len:]
|
||||
|
||||
ret += '\t\tDiscretionary data objects: %s\n' % ",".join(tokens)
|
||||
else:
|
||||
ret += '\t\tunhandled tag=0x%x body=%s\n' % (tag2, tag2Body.hex())
|
||||
|
||||
body = body[2+tag2Len:]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def selectResult(self, selectT, status, body):
|
||||
if not len(body):
|
||||
return ''
|
||||
|
||||
if selectT == 'FCP':
|
||||
return self.parseFcp(body)
|
||||
elif selectT == 'FCI':
|
||||
return self.parseFci(body)
|
||||
else:
|
||||
return '\tselectResult(fci=%s, status=0x%x) = %s\n' % (selectT, status, body.hex())
|
||||
|
||||
def getData(self, fileId, bytes):
|
||||
lc = bytes[4]
|
||||
tag = bytes[5]
|
||||
tagLen = bytes[6]
|
||||
|
||||
if tag == 0x5c:
|
||||
doStr = bytes[7:7+tagLen].hex()
|
||||
ret = '\tDO=%s\n' % DOs.get(doStr, "<%s>" % doStr)
|
||||
self.lastDo = doStr
|
||||
else:
|
||||
ret = '\tunknown tag=0%x len=%d v=%s' % (tag, tagLen, bytes[7:7+tagLen].hex())
|
||||
|
||||
return ret
|
||||
|
||||
def getDataResult(self, status, body):
|
||||
ret = ''
|
||||
'''
|
||||
while len(body) > 2:
|
||||
tag = body[0]
|
||||
tagLen = body[1]
|
||||
|
||||
ret += '\ttag=0x%x len=%d content=%s\n' % (tag, tagLen, body[2:2+tagLen].hex())
|
||||
|
||||
body = body[2+tagLen:]
|
||||
'''
|
||||
return ret
|
||||
|
||||
def mse(self, body):
|
||||
return body.hex()
|
||||
|
||||
def mseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponse(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def getResponseResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
def pso(self, body):
|
||||
return body.hex()
|
||||
|
||||
def psoResult(self, status, body):
|
||||
return body.hex()
|
||||
|
||||
|
||||
|
||||
def createAppByAid(aid):
|
||||
if aid in ("a000000308", 'a00000030800001000',):
|
||||
return ApplicationPIV(aid)
|
||||
|
||||
elif aid in ('a00000039742544659',):
|
||||
return ApplicationGids(aid)
|
||||
|
||||
elif aid in ('a0000001510000',):
|
||||
return ApplicationGpc(aid)
|
||||
|
||||
return ApplicationDummy(aid)
|
||||
|
||||
|
||||
def getErrorCode(status):
|
||||
if status & 0x6100:
|
||||
return "%d more bytes" % (status & 0xff)
|
||||
|
||||
return ERROR_CODES.get(status, "<unknown>")
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
fin = open(sys.argv[1], "r")
|
||||
else:
|
||||
fin = sys.stdin
|
||||
|
||||
lineno = 0
|
||||
lastCmd = 0
|
||||
lastSelect = None
|
||||
lastSelectFCI = False
|
||||
lastGetItem = None
|
||||
currentApp = None
|
||||
|
||||
for l in fin.readlines():
|
||||
lineno += 1
|
||||
|
||||
if not len(l):
|
||||
continue
|
||||
|
||||
# smartcard loggers have changed
|
||||
#if l.find("[DEBUG][com.freerdp.channels.smartcard.client]") == -1:
|
||||
# continue
|
||||
|
||||
body = ''
|
||||
recvKey = 'pbRecvBuffer: { '
|
||||
|
||||
pos = l.find(recvKey)
|
||||
if pos != -1:
|
||||
toCard = False
|
||||
|
||||
pos += len(recvKey)
|
||||
pos2 = l.find(' }', pos)
|
||||
if pos2 == -1:
|
||||
print("line %d: invalid recvBuffer")
|
||||
continue
|
||||
|
||||
else:
|
||||
toCard = True
|
||||
sendKey = 'pbSendBuffer: { '
|
||||
pos = l.find(sendKey)
|
||||
if pos == -1:
|
||||
continue
|
||||
pos += len(sendKey)
|
||||
|
||||
pos2 = l.find(' }', pos)
|
||||
if pos2 == -1:
|
||||
print("line %d: invalid sendBuffer")
|
||||
continue
|
||||
|
||||
body = l[pos:pos2]
|
||||
|
||||
print(l[0:-1])
|
||||
bytes = codecs.decode(body, 'hex')
|
||||
if toCard:
|
||||
(cla, ins, p1, p2) = bytes[0:4]
|
||||
cmdName = CMD_NAMES.get(ins, "<COMMAND 0x%x>" % ins)
|
||||
print(cmdName + ":")
|
||||
|
||||
if cmdName == "SELECT":
|
||||
lc = bytes[4]
|
||||
i = 5
|
||||
|
||||
if p1 == 0x00:
|
||||
print("\tselectByFID: %0.2x%0.2x" % (bytes[i], bytes[i+1]))
|
||||
i = i + lc
|
||||
|
||||
elif p1 == 0x4:
|
||||
aid = bytes[i:i+lc].hex()
|
||||
lastSelect = AIDs.get(aid, '<unknown %s>' % aid)
|
||||
print("\tselectByAID: %s(%s)" % (aid, lastSelect))
|
||||
|
||||
if p2 == 0x00:
|
||||
lastSelectT = "FCI"
|
||||
print('\tFCI')
|
||||
elif p2 == 0x04:
|
||||
print('\tFCP')
|
||||
lastSelectT = "FCP"
|
||||
elif p2 == 0x08:
|
||||
print('\tFMD')
|
||||
lastSelectT = "FMD"
|
||||
|
||||
if not currentApp or currentApp.getAID() != aid:
|
||||
currentApp = createAppByAid(aid)
|
||||
|
||||
|
||||
elif cmdName == "VERIFY":
|
||||
lc = bytes[4]
|
||||
P2_DATA_QUALIFIER = {
|
||||
0x00: "Card global password",
|
||||
0x01: "RFU",
|
||||
0x80: "Application password",
|
||||
0x81: "Application resetting password",
|
||||
0x82: "Application security status resetting code",
|
||||
}
|
||||
|
||||
pin=''
|
||||
if lc:
|
||||
pin = ", pin='" + bytes[5:5+lc-2].decode('utf8)') + "'"
|
||||
|
||||
print("\t%s%s" % (P2_DATA_QUALIFIER.get(p2, "<unknown>"), pin))
|
||||
|
||||
elif cmdName == "GET DATA":
|
||||
lc = bytes[4]
|
||||
fileId = p1 * 256 + p2
|
||||
|
||||
ret = currentApp.getData(fileId, bytes)
|
||||
print("%s" % ret)
|
||||
|
||||
elif cmdName == "GET RESPONSE":
|
||||
#lc = bytes[4]
|
||||
#fileId = p1 * 256 + p2
|
||||
|
||||
#ret = currentApp.getResponse(fileId, bytes)
|
||||
#print("%s" % ret)
|
||||
pass
|
||||
elif cmdName == "MSE":
|
||||
ret = currentApp.mse(bytes[5:5+lc])
|
||||
print("%s" % ret)
|
||||
|
||||
elif cmdName == "PSO":
|
||||
ret = currentApp.pso(bytes[5:5+lc])
|
||||
print("%s" % ret)
|
||||
else:
|
||||
print('handle %s' % cmdName)
|
||||
|
||||
lastCmd = cmdName
|
||||
|
||||
else:
|
||||
# Responses
|
||||
if not len(bytes):
|
||||
continue
|
||||
|
||||
status = bytes[-1] + bytes[-2] * 256
|
||||
body = bytes[0:-2]
|
||||
print("status=0x%0.4x(%s)" % (status, getErrorCode(status)))
|
||||
|
||||
if not len(body):
|
||||
continue
|
||||
|
||||
ret = ''
|
||||
if lastCmd == "SELECT":
|
||||
ret = currentApp.selectResult(lastSelectT, status, body)
|
||||
elif lastCmd == "GET DATA":
|
||||
ret = currentApp.getDataResult(status, body)
|
||||
elif lastCmd == "MSE":
|
||||
ret = currentApp.mseResult(status, body)
|
||||
elif lastCmd == "PSO":
|
||||
ret = currentApp.psoResult(status, body)
|
||||
elif lastCmd == "GET RESPONSE":
|
||||
ret = currentApp.getResponseResult(status, body)
|
||||
elif lastCmd == "VERIFY":
|
||||
ret = currentApp.verifyResult(status, body)
|
||||
|
||||
if ret:
|
||||
print("%s" % ret)
|
||||
412
third_party/FreeRDP/tools/update-settings-tests
vendored
Executable file
412
third_party/FreeRDP/tools/update-settings-tests
vendored
Executable file
@@ -0,0 +1,412 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
def get_values(entry_dict, entry_type):
|
||||
values = []
|
||||
if '*' == entry_type:
|
||||
for key in list(entry_dict.keys()):
|
||||
if entry_type in key:
|
||||
values += entry_dict[key]
|
||||
entry_dict.pop(key, None)
|
||||
elif entry_type in dict(entry_dict):
|
||||
values = entry_dict[entry_type]
|
||||
entry_dict.pop(entry_type, None)
|
||||
if values:
|
||||
return sorted(values)
|
||||
return values
|
||||
|
||||
def get_keys(entry_dict, entry_type, values):
|
||||
l = list()
|
||||
if '*' == entry_type:
|
||||
dval = dict()
|
||||
for key in list(entry_dict.keys()):
|
||||
if entry_type in key:
|
||||
xval = entry_dict[key]
|
||||
for val in xval:
|
||||
dval[val] = key
|
||||
|
||||
for val in list(values):
|
||||
key = dval[val]
|
||||
l.append(key)
|
||||
return l
|
||||
|
||||
def write_entry(f, entry_dict, entry_type, entry_name):
|
||||
values = get_values(entry_dict, entry_type)
|
||||
if not values:
|
||||
return
|
||||
|
||||
f.write('#define have_' + entry_name.lower() + '_list_indices\n')
|
||||
f.write('static const size_t ' + entry_name.lower() + '_list_indices[] =\n')
|
||||
f.write('{\n')
|
||||
|
||||
for val in values:
|
||||
f.write('\tFreeRDP_' + val + ',\n')
|
||||
|
||||
f.write('};\n\n')
|
||||
|
||||
def write_str_case(f, entry_idx, val):
|
||||
entry_types = ['BOOL', 'UINT16', 'INT16', 'UINT32', 'INT32', 'UINT64', 'INT64', 'STRING', 'POINTER']
|
||||
f.write('\t\t{FreeRDP_' + val + ', FREERDP_SETTINGS_TYPE_' + str(entry_types[entry_idx]) + ', "FreeRDP_' + val + '"},\n')
|
||||
|
||||
def write_str(f, entry_dict):
|
||||
f.write('typedef enum {\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_BOOL,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_UINT16,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_INT16,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_UINT32,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_INT32,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_UINT64,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_INT64,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_STRING,\n')
|
||||
f.write('\tFREERDP_SETTINGS_TYPE_POINTER\n')
|
||||
f.write('} FREERDP_SETTINGS_TYPE;\n')
|
||||
f.write('\n')
|
||||
f.write('struct settings_str_entry {\n')
|
||||
f.write('\tSSIZE_T id;\n')
|
||||
f.write('\tSSIZE_T type;\n')
|
||||
f.write('\tconst char* str;\n')
|
||||
f.write('};\n')
|
||||
f.write('static const struct settings_str_entry settings_map[] =\n')
|
||||
f.write('{\n')
|
||||
|
||||
entry_types = ['BOOL', 'UINT16', 'INT16', 'UINT32', 'INT32', 'UINT64', 'INT64', 'char*', '*']
|
||||
for entry_type in entry_types:
|
||||
values = get_values(entry_dict, entry_type)
|
||||
if values:
|
||||
for val in values:
|
||||
write_str_case(f, entry_types.index(entry_type), val)
|
||||
f.write('};\n\n')
|
||||
f.write('\n')
|
||||
|
||||
def write_getter_case(f, val, cast, typestr):
|
||||
f.write('\t\tcase ')
|
||||
if typestr:
|
||||
f.write('(' + typestr + ')')
|
||||
f.write('FreeRDP_' + val + ':\n')
|
||||
f.write('\t\t\treturn ' + cast + 'settings->' + val + ';\n\n')
|
||||
|
||||
def write_getter_body(f, values, ret, keys, isPointer, compat_values, typestr, entry_type):
|
||||
f.write('{\n')
|
||||
f.write('\tWINPR_ASSERT(settings);\n\n')
|
||||
f.write('\tswitch (id)\n')
|
||||
f.write('\t{\n')
|
||||
if values:
|
||||
for i in range(len(values)):
|
||||
val = values[i]
|
||||
cast = ''
|
||||
if isPointer and keys:
|
||||
key = keys[i]
|
||||
if key != 'void*':
|
||||
cast = '(void*)'
|
||||
write_getter_case(f, val, cast, None)
|
||||
if compat_values:
|
||||
f.write('#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)\n')
|
||||
for i in range(len(compat_values)):
|
||||
val = compat_values[i]
|
||||
cast = '(' + entry_type + ')'
|
||||
f.write('\t\t// API Compatibility section, remove with FreeRDP 4.x\n')
|
||||
write_getter_case(f, val, cast, typestr)
|
||||
f.write('#endif\n')
|
||||
f.write('\t\tdefault:\n')
|
||||
f.write('\t\t\tWLog_ERR(TAG, "Invalid key index %d [%s|%s]", id, freerdp_settings_get_name_for_key(id), freerdp_settings_get_type_name_for_key(id));\n')
|
||||
f.write('\t\t\tWINPR_ASSERT(FALSE);\n')
|
||||
f.write('\t\t\treturn ' + ret + ';\n')
|
||||
f.write('\t}\n')
|
||||
f.write('}\n\n')
|
||||
|
||||
def write_getter(f, entry_dict, entry_type, entry_name, postfix, compat_dict):
|
||||
isString = 'string' in entry_name
|
||||
isPointer = 'pointer' in entry_name
|
||||
copy = dict(entry_dict)
|
||||
values = get_values(entry_dict, entry_type)
|
||||
compat_values = get_values(compat_dict, entry_type)
|
||||
keys = get_keys(copy, entry_type, values)
|
||||
|
||||
typestr = 'FreeRDP_Settings_Keys_' + entry_name.capitalize()
|
||||
typestr = typestr.replace('_Uint', '_UInt')
|
||||
|
||||
if isPointer:
|
||||
f.write('void*')
|
||||
elif isString:
|
||||
f.write('const ' + entry_type)
|
||||
else:
|
||||
f.write(entry_type)
|
||||
|
||||
if isPointer:
|
||||
f.write(' freerdp_settings_get_pointer_writable(rdpSettings* settings, ' + typestr + ' id)\n')
|
||||
else:
|
||||
f.write(' freerdp_settings_get_' + entry_name.lower() + '(WINPR_ATTR_UNUSED const rdpSettings* settings, WINPR_ATTR_UNUSED ' + typestr + ' id)\n')
|
||||
if isString or isPointer:
|
||||
ret = 'nullptr';
|
||||
elif 'bool' in entry_name:
|
||||
ret = 'FALSE';
|
||||
else:
|
||||
ret = '0';
|
||||
|
||||
write_getter_body(f, values, ret, keys, isPointer, compat_values, typestr, entry_type)
|
||||
|
||||
if isString:
|
||||
f.write('char* freerdp_settings_get_' + entry_name.lower() + '_writable(rdpSettings* settings, ' + typestr + ' id)\n')
|
||||
write_getter_body(f, values, ret, keys, isPointer, compat_values, typestr, entry_type)
|
||||
|
||||
def write_setter_case(f, val, postfix, isPointer, cast):
|
||||
f.write('\t\tcase FreeRDP_' + val + ':\n')
|
||||
if isPointer:
|
||||
f.write('\t\t\tsettings->' + val + ' = ' + cast + ' cnv.v;\n')
|
||||
f.write('\t\t\tbreak;\n\n')
|
||||
elif not postfix:
|
||||
f.write('\t\t\tsettings->' + val + ' = ' + cast + ' cnv.c;\n')
|
||||
f.write('\t\t\tbreak;\n\n')
|
||||
elif len(postfix) <= 1:
|
||||
f.write('\t\t\treturn update_string' + postfix + '(&settings->' + val + ', cnv.c, len);\n\n')
|
||||
else:
|
||||
f.write('\t\t\treturn update_string' + postfix + '(&settings->' + val + ', cnv.cc, len, cleanup);\n\n')
|
||||
|
||||
def write_setter(f, entry_dict, entry_type, entry_name, postfix, compat_dict):
|
||||
isString = 'string' in entry_name
|
||||
isPointer = 'pointer' in entry_name
|
||||
copy = dict(entry_dict)
|
||||
values = get_values(entry_dict, entry_type)
|
||||
compat_values = get_values(compat_dict, entry_type)
|
||||
keys = get_keys(copy, entry_type, values)
|
||||
|
||||
typestr = 'FreeRDP_Settings_Keys_' + entry_name.capitalize()
|
||||
typestr = typestr.replace('_Uint', '_UInt')
|
||||
f.write('BOOL freerdp_settings_set_' + entry_name.lower())
|
||||
f.write(postfix)
|
||||
f.write('(WINPR_ATTR_UNUSED rdpSettings* settings, WINPR_ATTR_UNUSED ' + typestr + ' id, ')
|
||||
if isString or isPointer:
|
||||
f.write('const ')
|
||||
if not isPointer:
|
||||
f.write(entry_type + ' val')
|
||||
else:
|
||||
f.write('void* val')
|
||||
if isString and len(postfix) <= 1:
|
||||
f.write(', size_t len)\n')
|
||||
elif isString:
|
||||
f.write(', size_t len, BOOL cleanup)\n')
|
||||
else:
|
||||
f.write(')\n')
|
||||
f.write('{\n')
|
||||
f.write('\tunion\n')
|
||||
f.write('\t{\n')
|
||||
f.write('\t\tvoid* v;\n')
|
||||
f.write('\t\tconst void* cv;\n')
|
||||
if not isPointer:
|
||||
f.write(' ' + entry_type + ' c;\n')
|
||||
f.write(' const ' + entry_type + ' cc;\n')
|
||||
f.write('} cnv;\n')
|
||||
|
||||
f.write('\tWINPR_ASSERT(settings);\n\n')
|
||||
if isPointer:
|
||||
f.write('\tcnv.cv = val;\n\n')
|
||||
elif isString:
|
||||
f.write('\tcnv.cc = val;\n\n')
|
||||
else:
|
||||
f.write('\tcnv.c = val;\n\n')
|
||||
f.write('\tswitch (id)\n')
|
||||
f.write('\t{\n')
|
||||
if values:
|
||||
count = 0
|
||||
for val in values:
|
||||
index = count
|
||||
count += 1
|
||||
cast = ''
|
||||
if isPointer:
|
||||
k = keys[index];
|
||||
if k != 'void*' and len(k) > 0:
|
||||
cast = '(' + k + ')'
|
||||
write_setter_case(f, val, postfix, isPointer, cast)
|
||||
if compat_values:
|
||||
f.write('#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)\n')
|
||||
for val in compat_values:
|
||||
cast = '(int32_t)'
|
||||
f.write('\t\t// API Compatibility section, remove with FreeRDP 4.x\n')
|
||||
write_setter_case(f, val, postfix, isPointer, cast)
|
||||
f.write('#endif\n')
|
||||
f.write('\t\tdefault:\n')
|
||||
f.write('\t\t\tWLog_ERR(TAG, "Invalid key index %d [%s|%s]", id, freerdp_settings_get_name_for_key(id), freerdp_settings_get_type_name_for_key(id));\n')
|
||||
f.write('\t\t\treturn FALSE;\n')
|
||||
f.write('\t}\n')
|
||||
f.write('\treturn TRUE;\n')
|
||||
f.write('}\n\n')
|
||||
f.write('\n')
|
||||
if isString and len(postfix) <= 1:
|
||||
f.write('BOOL freerdp_settings_set_string_len(rdpSettings* settings, FreeRDP_Settings_Keys_String id, const char* val, size_t len)\n')
|
||||
f.write('{\n')
|
||||
f.write('\treturn freerdp_settings_set_string_copy_(settings, id, val, len, TRUE);\n')
|
||||
f.write('}\n')
|
||||
f.write('\n')
|
||||
|
||||
f.write('BOOL freerdp_settings_set_string(rdpSettings* settings, FreeRDP_Settings_Keys_String id, const char* val)\n')
|
||||
f.write('{\n')
|
||||
f.write('\tsize_t len = 0;\n')
|
||||
f.write('\tif (val) len = strlen(val);\n')
|
||||
f.write('\treturn freerdp_settings_set_string_copy_(settings, id, val, len, TRUE);\n')
|
||||
f.write('}\n')
|
||||
f.write('\n')
|
||||
|
||||
name = os.path.dirname(os.path.realpath(__file__))
|
||||
begin = "WARNING: this data structure is carefully padded for ABI stability!"
|
||||
end = "WARNING: End of ABI stable zone!"
|
||||
|
||||
print('begin parsing settings header')
|
||||
try:
|
||||
type_list = dict()
|
||||
|
||||
with open(name + "/../include/freerdp/settings_types_private.h", "r") as f:
|
||||
lines = f.readlines()
|
||||
started = False
|
||||
for line in lines:
|
||||
if not started:
|
||||
if begin in line:
|
||||
started = True
|
||||
continue
|
||||
|
||||
if end in line:
|
||||
break
|
||||
|
||||
sline = line.strip()
|
||||
if not sline:
|
||||
continue
|
||||
if sline.startswith('/'):
|
||||
continue
|
||||
if sline.startswith('*'):
|
||||
continue
|
||||
if 'padding' in sline:
|
||||
continue
|
||||
if 'version' in sline:
|
||||
continue
|
||||
|
||||
if sline.startswith('SETTINGS_DEPRECATED(ALIGN64'):
|
||||
sline = sline[27:].strip()
|
||||
|
||||
sline = sline[:sline.find(');')]
|
||||
pair = sline.split()
|
||||
if pair[0] in type_list:
|
||||
type_list[pair[0]].append(pair[1])
|
||||
else:
|
||||
type_list[pair[0]] = [pair[1]]
|
||||
|
||||
with open(name + '/../libfreerdp/common/settings_getters.c', 'w+') as f:
|
||||
f.write('/* Generated by ' + '' + ' */\n\n')
|
||||
f.write('#include "../core/settings.h"\n\n')
|
||||
f.write('#include <winpr/assert.h>\n')
|
||||
f.write('#include <freerdp/settings.h>\n')
|
||||
f.write('#include <freerdp/log.h>\n\n')
|
||||
f.write('#define TAG FREERDP_TAG("common.settings")\n\n')
|
||||
|
||||
f.write('static void free_string(char** current, BOOL cleanup)\n')
|
||||
f.write('{\n')
|
||||
f.write('\tif (cleanup)\n')
|
||||
f.write('\t{\n')
|
||||
f.write('\t\tif (*current)\n')
|
||||
f.write('\t\t\tmemset(*current, 0, strlen(*current));\n')
|
||||
f.write('\t\tfree(*current);\n')
|
||||
f.write('\t\t(*current) = nullptr;\n')
|
||||
f.write('\t}\n')
|
||||
f.write('}\n\n')
|
||||
|
||||
f.write('static BOOL alloc_empty_string(char** current, const char* next, size_t next_len)\n')
|
||||
f.write('{\n')
|
||||
f.write('\tif (!next && (next_len > 0))\n')
|
||||
f.write('\t{\n')
|
||||
f.write('\t\t*current = calloc(next_len, 1);\n')
|
||||
f.write('\t\treturn (*current != nullptr);\n')
|
||||
f.write('\t}\n')
|
||||
f.write('\treturn FALSE;\n')
|
||||
f.write('}\n\n')
|
||||
|
||||
|
||||
f.write('static BOOL update_string_copy_(char** current, const char* next, size_t next_len, BOOL cleanup)\n')
|
||||
f.write('{\n')
|
||||
f.write('\tfree_string(current, cleanup);\n')
|
||||
f.write('\n')
|
||||
f.write('\tif (alloc_empty_string(current, next, next_len))\n')
|
||||
f.write('\t\treturn TRUE;\n')
|
||||
f.write('\n')
|
||||
f.write('\t*current = (next ? strndup(next, next_len) : nullptr);\n')
|
||||
f.write('\treturn !next || (*current != nullptr);\n')
|
||||
f.write('}\n\n')
|
||||
|
||||
f.write('static BOOL update_string_(char** current, char* next, size_t next_len)\n')
|
||||
f.write('{\n')
|
||||
f.write('\tfree_string(current, TRUE);\n')
|
||||
f.write('\n')
|
||||
f.write('\tif (alloc_empty_string(current, next, next_len))\n')
|
||||
f.write('\t\treturn TRUE;\n')
|
||||
f.write('\n')
|
||||
f.write('\t*current = next;\n')
|
||||
f.write('\treturn !next || (*current != nullptr);\n')
|
||||
f.write('}\n\n')
|
||||
|
||||
getter_list = dict(type_list)
|
||||
setter_list = dict(type_list)
|
||||
setter_list2 = dict(type_list)
|
||||
# Compatibility with older 3.x releases where the value was wrongly an unsigned type
|
||||
compat_getter_list = dict()
|
||||
compat_getter_list['UINT32'] = list()
|
||||
compat_getter_list['UINT32'].append('MonitorLocalShiftX')
|
||||
compat_getter_list['UINT32'].append('MonitorLocalShiftY')
|
||||
|
||||
compat_setter_list = dict(compat_getter_list)
|
||||
|
||||
write_getter(f, getter_list, 'BOOL', 'bool', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'BOOL', 'bool', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'UINT16', 'uint16', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'UINT16', 'uint16', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'INT16', 'int16', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'INT16', 'int16', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'UINT32', 'uint32', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'UINT32', 'uint32', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'INT32', 'int32', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'INT32', 'int32', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'UINT64', 'uint64', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'UINT64', 'uint64', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'INT64', 'int64', '', compat_getter_list)
|
||||
write_setter(f, setter_list, 'INT64', 'int64', '', compat_setter_list)
|
||||
write_getter(f, getter_list, 'char*', 'string', '_', compat_getter_list)
|
||||
write_setter(f, setter_list, 'char*', 'string', '_', compat_setter_list)
|
||||
write_setter(f, setter_list2, 'char*', 'string', '_copy_', compat_setter_list)
|
||||
write_getter(f, getter_list, '*', 'pointer', '', compat_getter_list)
|
||||
write_setter(f, setter_list, '*', 'pointer', '', compat_setter_list)
|
||||
|
||||
f.write('\n')
|
||||
|
||||
with open(name + '/../libfreerdp/common/settings_str.h', 'w+') as f:
|
||||
f.write('/* Generated by ' + '' + ' */\n\n')
|
||||
f.write('#ifndef FREERDP_CORE_SETTINGS_STR_H\n')
|
||||
f.write('#define FREERDP_CORE_SETTINGS_STR_H\n\n')
|
||||
f.write('#include "../core/settings.h"\n\n')
|
||||
f.write('#include <freerdp/settings.h>\n')
|
||||
f.write('#include <freerdp/log.h>\n\n')
|
||||
f.write('#define TAG FREERDP_TAG("common.settings")\n\n')
|
||||
|
||||
getter_list = dict(type_list)
|
||||
write_str(f, getter_list)
|
||||
f.write('#endif\n')
|
||||
f.write('\n')
|
||||
|
||||
|
||||
with open(name + '/../libfreerdp/core/test/settings_property_lists.h', 'w+') as f:
|
||||
f.write('#ifndef TEST_SETTINGS_PROPERTY_LISTS\n')
|
||||
f.write('#define TEST_SETTINGS_PROPERTY_LISTS\n\n')
|
||||
|
||||
write_entry(f, type_list, 'BOOL', 'bool')
|
||||
write_entry(f, type_list, 'UINT16', 'uint16')
|
||||
write_entry(f, type_list, 'INT16', 'int16')
|
||||
write_entry(f, type_list, 'UINT32', 'uint32')
|
||||
write_entry(f, type_list, 'INT32', 'int32')
|
||||
write_entry(f, type_list, 'UINT64', 'uint64')
|
||||
write_entry(f, type_list, 'INT64', 'int64')
|
||||
write_entry(f, type_list, 'char*', 'string')
|
||||
write_entry(f, type_list, '*', 'pointer')
|
||||
|
||||
f.write('#endif /* TEST_SETTINGS_PROPERTY_LISTS */\n\n')
|
||||
|
||||
print('remaining:\n' + str(type_list))
|
||||
except IOError as e:
|
||||
print('failed to parse settings header ' + str(e))
|
||||
sys.exit(-1)
|
||||
print('ended parsing settings header')
|
||||
709
third_party/FreeRDP/tools/wireshark/rdp-udp.lua
vendored
Normal file
709
third_party/FreeRDP/tools/wireshark/rdp-udp.lua
vendored
Normal file
@@ -0,0 +1,709 @@
|
||||
--[[
|
||||
RDP UDP transport dissector for wireshark
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
--]]
|
||||
|
||||
local sslDissector = Dissector.get ("tls")
|
||||
local dtlsDissector = Dissector.get ("dtls")
|
||||
|
||||
local dprint = function(...)
|
||||
print(table.concat({"Lua:", ...}," "))
|
||||
end
|
||||
|
||||
local dprint2 = dprint
|
||||
|
||||
dprint2("loading RDP-UDP with wireshark=", get_version())
|
||||
|
||||
local rdpudp = Proto("rdpudp", "UDP transport for RDP")
|
||||
|
||||
-- UDP1 fields
|
||||
local pf_udp_snSourceAck = ProtoField.uint32("rdpudp.snsourceack", "snSourceAck", base.HEX)
|
||||
local pf_udp_ReceiveWindowSize = ProtoField.uint16("rdpudp.receivewindowsize", "ReceiveWindowSize", base.DEC)
|
||||
local pf_udp_flags = ProtoField.uint16("rdpudp.flags", "Flags", base.HEX)
|
||||
|
||||
RDPUDP_SYN = 0x0001
|
||||
RDPUDP_FIN = 0x0002
|
||||
RDPUDP_ACK = 0x0004
|
||||
RDPUDP_DATA = 0x0008
|
||||
RDPUDP_FEC = 0x0010
|
||||
RDPUDP_CN = 0x0020
|
||||
RDPUDP_CWR = 0x0040
|
||||
RDPUDP_AOA = 0x0100
|
||||
RDPUDP_SYNLOSSY = 0x0200
|
||||
RDPUDP_ACKDELAYED = 0x0400
|
||||
RDPUDP_CORRELATIONID = 0x0800
|
||||
RDPUDP_SYNEX = 0x1000
|
||||
|
||||
local pf_udp_flag_syn = ProtoField.bool("rdpudp.flags.syn", "Syn", base.HEX, nil, RDPUDP_SYN)
|
||||
local pf_udp_flag_fin = ProtoField.bool("rdpudp.flags.fin", "Fin", base.HEX, nil, RDPUDP_FIN)
|
||||
local pf_udp_flag_ack = ProtoField.bool("rdpudp.flags.ack", "Ack", base.HEX, nil, RDPUDP_ACK)
|
||||
local pf_udp_flag_data = ProtoField.bool("rdpudp.flags.data", "Data", base.HEX, nil, RDPUDP_DATA)
|
||||
local pf_udp_flag_fec = ProtoField.bool("rdpudp.flags.fec", "FECData", base.HEX, nil, RDPUDP_FEC)
|
||||
local pf_udp_flag_cn = ProtoField.bool("rdpudp.flags.cn", "CN", base.HEX, nil, RDPUDP_CN)
|
||||
local pf_udp_flag_cwr = ProtoField.bool("rdpudp.flags.cwr", "CWR", base.HEX, nil, RDPUDP_CWR)
|
||||
local pf_udp_flag_aoa = ProtoField.bool("rdpudp.flags.aoa", "Ack of Acks", base.HEX, nil, RDPUDP_AOA)
|
||||
local pf_udp_flag_synlossy = ProtoField.bool("rdpudp.flags.synlossy", "Syn lossy", base.HEX, nil, RDPUDP_SYNLOSSY)
|
||||
local pf_udp_flag_ackdelayed = ProtoField.bool("rdpudp.flags.ackdelayed", "Ack delayed", base.HEX, nil, RDPUDP_ACKDELAYED)
|
||||
local pf_udp_flag_correlationId = ProtoField.bool("rdpudp.flags.correlationid", "Correlation id", base.HEX, nil, RDPUDP_CORRELATIONID)
|
||||
local pf_udp_flag_synex = ProtoField.bool("rdpudp.flags.synex", "SynEx", base.HEX, nil, RDPUDP_SYNEX)
|
||||
|
||||
local pf_udp_snInitialSequenceNumber = ProtoField.uint32("rdpudp.initialsequencenumber", "Initial SequenceNumber", base.HEX)
|
||||
local pf_udp_upstreamMtu = ProtoField.uint16("rdpudp.upstreammtu", "Upstream MTU", base.DEC)
|
||||
local pf_udp_downstreamMtu = ProtoField.uint16("rdpudp.downstreammtu", "DownStream MTU", base.DEC)
|
||||
|
||||
local pf_udp_correlationId = ProtoField.new("Correlation Id", "rdpudp.correlationid", ftypes.BYTES)
|
||||
|
||||
local pf_udp_synex_flags = ProtoField.uint16("rdpudp.synex.flags", "Flags", base.HEX)
|
||||
local pf_udp_synex_flag_version = ProtoField.bool("rdpudp.synex.flags.versioninfo", "Version info", base.HEX, nil, 0x0001)
|
||||
local pf_udp_synex_version = ProtoField.uint16("rdpudp.synex.version", "Version", base.HEX, {[1]="Version 1", [2]="Version 2", [0x101]="Version 3"})
|
||||
local pf_udp_synex_cookiehash = ProtoField.new("Cookie Hash", "rdpudp.synex.cookiehash", ftypes.BYTES)
|
||||
|
||||
local pf_udp_ack_vectorsize = ProtoField.uint16("rdpudp.ack.vectorsize", "uAckVectorSize", base.DEC)
|
||||
local pf_udp_ack_item = ProtoField.uint8("rdpudp.ack.item", "Ack item", base.HEX)
|
||||
local pf_udp_ack_item_state = ProtoField.uint8("rdpudp.ack.item.state", "VECTOR_ELEMENT_STATE", base.HEX, {[0]="Received", [1]="Reserved 1", [2]="Reserved 2", [3]="Pending"}, 0xc0)
|
||||
local pf_udp_ack_item_rle = ProtoField.uint8("rdpudp.ack.item.rle", "Run length", base.DEC, nil, 0x3f)
|
||||
|
||||
local pf_udp_fec_coded = ProtoField.uint32("rdpudp.fec.coded", "snCoded", base.HEX)
|
||||
local pf_udp_fec_sourcestart = ProtoField.uint32("rdpudp.fec.sourcestart", "snSourceStart", base.HEX)
|
||||
local pf_udp_fec_range = ProtoField.uint8("rdpudp.fec.range", "Range", base.DEC)
|
||||
local pf_udp_fec_fecindex = ProtoField.uint8("rdpudp.fec.fecindex", "Fec index", base.HEX)
|
||||
|
||||
local pf_udp_resetseqenum = ProtoField.uint32("rdpudp.resetSeqNum", "snResetSeqNum", base.HEX)
|
||||
|
||||
local pf_udp_source_sncoded = ProtoField.uint32("rdpudp.data.sncoded", "snCoded", base.HEX)
|
||||
local pf_udp_source_snSourceStart = ProtoField.uint32("rdpudp.data.sourceStart", "snSourceStart", base.HEX)
|
||||
|
||||
|
||||
-- UDP2 fields
|
||||
local pf_PacketPrefixByte = ProtoField.new("PacketPrefixByte", "rdpudp2.prefixbyte", ftypes.UINT8, nil, base.HEX)
|
||||
|
||||
local pf_packetType = ProtoField.uint8("rdpudp2.packetType", "PacketType", base.HEX, {[0] = "Data", [8] = "Dummy"}, 0x1e, "type of packet")
|
||||
|
||||
RDPUDP2_ACK = 0x0001
|
||||
RDPUDP2_DATA = 0x0004
|
||||
RDPUDP2_ACKVEC = 0x0008
|
||||
RDPUDP2_AOA = 0x0010
|
||||
RDPUDP2_OVERHEAD = 0x0040
|
||||
RDPUDP2_DELAYACK = 0x00100
|
||||
|
||||
local pf_flags = ProtoField.uint16("rdpudp2.flags", "Flags", base.HEX, nil, 0xfff, "flags")
|
||||
local pf_flag_ack = ProtoField.bool("rdpudp2.flags.ack", "Ack", base.HEX, nil, RDPUDP2_ACK, "packet contains Ack payload")
|
||||
local pf_flag_data = ProtoField.bool("rdpudp2.flags.data", "Data", base.HEX, nil, RDPUDP2_DATA, "packet contains Data payload")
|
||||
local pf_flag_ackvec = ProtoField.bool("rdpudp2.flags.ackvec", "AckVec", base.HEX, nil, RDPUDP2_ACKVEC, "packet contains AckVec payload")
|
||||
local pf_flag_aoa = ProtoField.bool("rdpudp2.flags.ackofacks", "AckOfAcks", base.HEX, nil, RDPUDP2_AOA, "packet contains AckOfAcks payload")
|
||||
local pf_flag_overhead = ProtoField.bool("rdpudp2.flags.overheadsize", "OverheadSize", base.HEX, nil, RDPUDP2_OVERHEAD, "packet contains OverheadSize payload")
|
||||
local pf_flag_delayackinfo = ProtoField.bool("rdpudp2.flags.delayackinfo", "DelayedAckInfo", base.HEX, nil, RDPUDP2_DELAYACK, "packet contains DelayedAckInfo payload")
|
||||
|
||||
local pf_logWindow = ProtoField.uint16("rdpudp2.logWindow", "LogWindow", base.DEC, nil, 0xf000, "flags")
|
||||
|
||||
local pf_AckSeq = ProtoField.uint16("rdpudp2.ack.seqnum", "Base Seq", base.HEX)
|
||||
local pf_AckTs = ProtoField.uint24("rdpudp2.ack.ts", "receivedTS", base.DEC)
|
||||
local pf_AckSendTimeGap = ProtoField.uint8("rdpudp2.ack.sendTimeGap", "sendTimeGap", base.DEC)
|
||||
local pf_ndelayedAcks = ProtoField.uint8("rdpudp2.ack.numDelayedAcks", "NumDelayedAcks", base.DEC, nil, 0x0f)
|
||||
local pf_delayedTimeScale = ProtoField.uint8("rdpudp2.ack.delayedTimeScale", "delayedTimeScale", base.DEC, nil, 0xf0)
|
||||
local pf_delayedAcks = ProtoField.new("Delayed acks", "rdpudp2.ack.delayedAcks", ftypes.BYTES)
|
||||
local pf_delayedAck = ProtoField.uint8("rdpudp2.ack.delayedAck", "Delayed ack", base.DEC)
|
||||
|
||||
local pf_OverHeadSize = ProtoField.uint8("rdpudp2.overheadsize", "Overhead size", base.DEC)
|
||||
|
||||
local pf_DelayAckMax = ProtoField.uint8("rdpudp2.delayackinfo.max", "MaxDelayedAcks", base.DEC)
|
||||
local pf_DelayAckTimeout = ProtoField.uint8("rdpudp2.delayackinfo.timeout", "DelayedAckTimeoutInMs", base.DEC)
|
||||
|
||||
local pf_AckOfAcks = ProtoField.uint16("rdpudp2.ackofacks", "Ack of Acks", base.HEX)
|
||||
|
||||
local pf_DataSeqNumber = ProtoField.uint16("rdpudp2.data.seqnum", "sequence number", base.HEX)
|
||||
local pf_DataChannelSeqNumber = ProtoField.uint16("rdpudp2.data.channelseqnumber", "Channel sequence number", base.HEX)
|
||||
local pf_Data = ProtoField.new("Data", "rdpudp2.data", ftypes.BYTES)
|
||||
|
||||
local pf_AckvecBaseSeq = ProtoField.uint16("rdpudp2.ackvec.baseseqnum", "Base sequence number", base.HEX)
|
||||
local pf_AckvecCodecAckVecSize = ProtoField.uint16("rdpudp2.ackvec.codecackvecsize", "Codec ackvec size", base.DEC, nil, 0x7f)
|
||||
local pf_AckvecHaveTs = ProtoField.bool("rdpudp2.ackvec.havets", "have timestamp", base.DEC, nil, 0x80)
|
||||
local pf_AckvecTimeStamp = ProtoField.uint24("rdpudp2.ackvec.timestamp", "Timestamp", base.HEX)
|
||||
local pf_AckvecCodedAck = ProtoField.uint8("rdpudp2.ackvec.codecAck", "Coded Ack", base.HEX)
|
||||
local pf_AckvecCodedAckMode = ProtoField.uint8("rdpudp2.ackvec.codecAckMode", "Mode", base.HEX, {[0]="Bitmap", [1]="Run length"}, 0x80)
|
||||
local pf_AckvecCodedAckRleState = ProtoField.uint8("rdpudp2.ackvec.codecAckRleState", "State", base.HEX, {[0]="lost",[1]="received"}, 0x40)
|
||||
local pf_AckvecCodedAckRleLen = ProtoField.uint8("rdpudp2.ackvec.codecAckRleLen", "Length", base.DEC, nil, 0x3f)
|
||||
|
||||
rdpudp.fields = {
|
||||
-- UDP1
|
||||
pf_udp_snSourceAck, pf_udp_ReceiveWindowSize, pf_udp_flags, pf_udp_flag_syn,
|
||||
pf_udp_flag_fin, pf_udp_flag_ack, pf_udp_flag_data, pf_udp_flag_fec, pf_udp_flag_cn,
|
||||
pf_udp_flag_cwr, pf_udp_flag_aoa, pf_udp_flag_synlossy, pf_udp_flag_ackdelayed,
|
||||
pf_udp_flag_correlationId, pf_udp_flag_synex,
|
||||
pf_udp_snInitialSequenceNumber, pf_udp_upstreamMtu, pf_udp_downstreamMtu,
|
||||
pf_udp_correlationId,
|
||||
pf_udp_synex_flags, pf_udp_synex_flag_version, pf_udp_synex_version, pf_udp_synex_cookiehash,
|
||||
pf_udp_ack_vectorsize, pf_udp_ack_item, pf_udp_ack_item_state, pf_udp_ack_item_rle,
|
||||
pf_udp_fec_coded, pf_udp_fec_sourcestart, pf_udp_fec_range, pf_udp_fec_fecindex,
|
||||
pf_udp_resetseqenum,
|
||||
pf_udp_source_sncoded, pf_udp_source_snSourceStart,
|
||||
|
||||
-- UDP2
|
||||
pf_PacketPrefixByte, pf_packetType,
|
||||
pf_flags, pf_flag_ack, pf_flag_data, pf_flag_ackvec, pf_flag_aoa, pf_flag_overhead, pf_flag_delayackinfo,
|
||||
pf_logWindow,
|
||||
pf_Ack, pf_AckSeq, pf_AckTs, pf_AckSendTimeGap, pf_ndelayedAcks, pf_delayedTimeScale, pf_delayedAcks,
|
||||
pf_OverHeadSize,
|
||||
pf_DelayAckMax, pf_DelayAckTimeout,
|
||||
pf_AckOfAcks,
|
||||
pf_DataSeqNumber, pf_Data, pf_DataChannelSeqNumber,
|
||||
pf_Ackvec, pf_AckvecBaseSeq, pf_AckvecCodecAckVecSize, pf_AckvecHaveTs, pf_AckvecTimeStamp, pf_AckvecCodedAck, pf_AckvecCodedAckMode,
|
||||
pf_AckvecCodedAckRleState, pf_AckvecCodedAckRleLen
|
||||
}
|
||||
|
||||
rdpudp.prefs.track_udp2_peer_states = Pref.bool("Track state of UDP2 peers", true, "Keep track of state of UDP2 peers (receiver and sender windows")
|
||||
rdpudp.prefs.debug_ssl = Pref.bool("SSL debug message", false, "print verbose message of the SSL fragments reassembly")
|
||||
|
||||
|
||||
|
||||
local field_rdpudp_flags = Field.new("rdpudp.flags")
|
||||
local field_rdpudp2_packetType = Field.new("rdpudp2.packetType")
|
||||
local field_rdpudp2_channelSeqNumber = Field.new("rdpudp2.data.channelseqnumber")
|
||||
local field_rdpudp2_ackvec_base = Field.new("rdpudp2.ackvec.baseseqnum")
|
||||
|
||||
function unwrapPacket(tvbuf)
|
||||
local len = tvbuf:reported_length_remaining()
|
||||
local ret = tvbuf:bytes(7, 1) .. tvbuf:bytes(1, 6) .. tvbuf:bytes(0, 1) .. tvbuf:bytes(8, len-8)
|
||||
--dprint2("input first bytes=", tvbuf:bytes(0, 9):tohex(true, " "))
|
||||
--dprint2("output first bytes=", ret:subset(0, 9):tohex(true, " "))
|
||||
return ret:tvb("RDP-UDP unwrapped")
|
||||
end
|
||||
|
||||
function rdpudp.init()
|
||||
udpComms = {}
|
||||
end
|
||||
|
||||
function computePacketKey(pktinfo)
|
||||
local addr_lo = pktinfo.net_src
|
||||
local addr_hi = pktinfo.net_dst
|
||||
local port_lo = pktinfo.src_port
|
||||
local port_hi = pktinfo.dst_port
|
||||
|
||||
if addr_lo > addr_hi then
|
||||
addr_hi, addr_lo = addr_lo, addr_hi
|
||||
port_hi, port_lo = port_lo, port_hi
|
||||
end
|
||||
|
||||
return tostring(addr_lo) .. ":" .. tostring(port_lo) .. " -> " .. tostring(addr_hi) .. ":" .. tostring(port_hi)
|
||||
end
|
||||
|
||||
function tableItem(pktinfo)
|
||||
local key = computePacketKey(pktinfo)
|
||||
local ret = udpComms[key]
|
||||
if ret == nil then
|
||||
dprint2(pktinfo.number .. " creating entry for " .. key)
|
||||
udpComms[key] = { isLossy = false, switchToUdp2 = nil, sslFragments = {},
|
||||
serverAddr = nil, clientAddr = nil,
|
||||
serverState = { receiveLow = nil, receiveHigh = nil, senderLow = nil, senderHigh = nil },
|
||||
clientState = { receiveLow = nil, receiveHigh = nil, senderLow = nil, senderHigh = nil }
|
||||
}
|
||||
ret = udpComms[key]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function doAlign(v, alignment)
|
||||
local rest = v % alignment
|
||||
if rest ~= 0 then
|
||||
return v + alignment - rest
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
function dissectV1(tvbuf, pktinfo, tree)
|
||||
--dprint2("dissecting in UDP1 mode")
|
||||
local pktlen = tvbuf:reported_length_remaining()
|
||||
|
||||
tree:add(pf_udp_snSourceAck, tvbuf:range(0, 4))
|
||||
tree:add(pf_udp_ReceiveWindowSize, tvbuf:range(4, 2))
|
||||
local flagsRange = tvbuf:range(6, 2)
|
||||
local flagsItem = tree:add(pf_udp_flags, flagsRange)
|
||||
--
|
||||
flagsItem:add(pf_udp_flag_syn, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_fin, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_ack, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_data, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_fec, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_cn, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_cwr, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_aoa, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_synlossy, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_ackdelayed, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_correlationId, flagsRange)
|
||||
flagsItem:add(pf_udp_flag_synex, flagsRange)
|
||||
|
||||
startAt = 8
|
||||
local flags = flagsRange:uint()
|
||||
local haveSyn = bit32.band(flags, RDPUDP_SYN) ~= 0
|
||||
local haveAck = bit32.band(flags, RDPUDP_ACK) ~= 0
|
||||
local isLossySyn = bit32.band(flags, RDPUDP_SYNLOSSY) ~= 0
|
||||
local tableRecord = tableItem(pktinfo)
|
||||
|
||||
|
||||
if isLossySyn then
|
||||
tableRecord.isLossy = true
|
||||
end
|
||||
|
||||
if haveSyn then
|
||||
-- dprint2("rdpudp - SYN")
|
||||
local synItem = tree:add("Syn", tvbuf:range(startAt, 8))
|
||||
|
||||
synItem:add(pf_udp_snInitialSequenceNumber, tvbuf:range(startAt, 4))
|
||||
synItem:add(pf_udp_upstreamMtu, tvbuf:range(startAt+4, 2))
|
||||
synItem:add(pf_udp_downstreamMtu, tvbuf:range(startAt+6, 2))
|
||||
|
||||
startAt = startAt + 8
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP_CORRELATIONID) ~= 0 then
|
||||
-- dprint2("rdpudp - CorrelationId")
|
||||
tree:add(pf_udp_correlationId, tvbuf:range(startAt, 16))
|
||||
startAt = startAt + 32
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP_SYNEX) ~= 0 then
|
||||
-- dprint2("rdpudp - SynEx")
|
||||
local synexItem = tree:add("SynEx")
|
||||
|
||||
local synexFlagsRange = tvbuf:range(startAt, 2)
|
||||
local synexFlags = synexItem:add(pf_udp_synex_flags, synexFlagsRange);
|
||||
--
|
||||
synexFlags:add(pf_udp_synex_flag_version, synexFlagsRange)
|
||||
local exflags = synexFlagsRange:uint()
|
||||
startAt = startAt + 2
|
||||
if bit32.band(exflags, 1) ~= 0 then
|
||||
synexItem:add(pf_udp_synex_version, tvbuf:range(startAt, 2))
|
||||
local versionVal = tvbuf:range(startAt, 2):uint()
|
||||
startAt = startAt + 2
|
||||
|
||||
if versionVal == 0x101 then
|
||||
if not haveAck then
|
||||
synexItem:add(pf_udp_synex_cookiehash, tvbuf:range(startAt, 32))
|
||||
startAt = startAt + 32
|
||||
else
|
||||
-- switch to UDP2
|
||||
tableRecord.switchToUdp2 = pktinfo.number
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local mask = RDPUDP_SYN + RDPUDP_ACK
|
||||
if bit32.band(flags, mask) == mask then
|
||||
tableRecord.serverAddr = tostring(pktinfo.net_src)
|
||||
tableRecord.clientAddr = tostring(pktinfo.net_dst)
|
||||
-- dprint2(pktinfo.number .. ": key='" .. computePacketKey(pktinfo) ..
|
||||
-- "' setting server=" .. tableRecord.serverAddr .. " client=" .. tableRecord.clientAddr)
|
||||
end
|
||||
end
|
||||
|
||||
if haveAck and not haveSyn then
|
||||
-- dprint2("rdpudp - Ack")
|
||||
local ackItem = tree:add("Ack")
|
||||
ackItem:add(pf_udp_ack_vectorsize, tvbuf:range(startAt, 2))
|
||||
|
||||
local i = 0
|
||||
uAckVectorSize = tvbuf:range(startAt, 2):uint()
|
||||
while i < uAckVectorSize do
|
||||
local ackRange = tvbuf:range(startAt + 2 + i, 1)
|
||||
local ack = ackItem:add(pf_udp_ack_item, ackRange)
|
||||
ack:add(pf_udp_ack_item_state, ackRange)
|
||||
ack:add(pf_udp_ack_item_rle, ackRange)
|
||||
i = i + 1
|
||||
end -- while
|
||||
|
||||
-- aligned on a dword (4 bytes) boundary
|
||||
-- dprint2("pktinfo=",pktinfo.number," blockSz=",doAlign(2 + uAckVectorSize, 4))
|
||||
startAt = startAt + doAlign(2 + uAckVectorSize, 4)
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP_FEC) ~= 0 then
|
||||
-- dprint2("rdpudp - FEC header")
|
||||
local fecItem = tree:add("FEC", tvbuf:range(startAt, 12))
|
||||
fecItem:add(pf_udp_fec_coded, tvbuf:range(startAt, 4))
|
||||
fecItem:add(pf_udp_fec_sourcestart, tvbuf:range(startAt+4, 4))
|
||||
fecItem:add(pf_udp_fec_range, tvbuf:range(startAt+8, 1))
|
||||
fecItem:add(pf_udp_fec_fecindex, tvbuf:range(startAt+9, 1))
|
||||
|
||||
startAt = startAt + (4 * 3)
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP_AOA) ~= 0 then
|
||||
-- dprint2("rdpudp - AOA")
|
||||
tree:add(pf_udp_resetseqenum, tvbuf:range(startAt, 4))
|
||||
startAt = startAt + 4
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP_DATA) ~= 0 then
|
||||
-- dprint2("rdpudp - Data")
|
||||
local dataItem = tree:add("Data")
|
||||
dataItem:add(pf_udp_source_sncoded, tvbuf:range(startAt, 4))
|
||||
dataItem:add(pf_udp_source_snSourceStart, tvbuf:range(startAt+4, 4))
|
||||
startAt = startAt + 8
|
||||
|
||||
local payload = tvbuf:range(startAt)
|
||||
local subTvb = payload:tvb("payload")
|
||||
if tableRecord.isLossy then
|
||||
dtlsDissector:call(subTvb, pktinfo, dataItem)
|
||||
else
|
||||
sslDissector:call(subTvb, pktinfo, dataItem)
|
||||
end
|
||||
end
|
||||
|
||||
return pktlen
|
||||
end
|
||||
|
||||
-- given a tvb containing SSL records returns the part of the buffer that has complete
|
||||
-- SSL records
|
||||
function getCompleteSslRecordsLen(tvb)
|
||||
local startAt = 0
|
||||
local remLen = tvb:reported_length_remaining()
|
||||
|
||||
while remLen > 5 do
|
||||
local recordLen = 5 + tvb:range(startAt+3, 2):uint()
|
||||
if remLen < recordLen then
|
||||
break
|
||||
end
|
||||
startAt = startAt + recordLen
|
||||
remLen = remLen - recordLen
|
||||
end -- while
|
||||
|
||||
return startAt;
|
||||
end
|
||||
|
||||
TLS_OK = 0
|
||||
TLS_SHORT = 1
|
||||
TLS_NOT_TLS = 2
|
||||
TLS_NOT_COMPLETE = 3
|
||||
sslResNames = {[0]="TLS_OK", [1]="TLS_SHORT", [2]="TLS_NOT_TLS", [3]="TLS_NOT_COMPLETE"}
|
||||
|
||||
function checkSslRecord(tvb)
|
||||
local remLen = tvb:reported_length_remaining()
|
||||
|
||||
if remLen <= 5 then
|
||||
return TLS_SHORT, 0
|
||||
end
|
||||
|
||||
local b0 = tvb:range(0, 1):uint()
|
||||
if b0 < 0x14 or b0 > 0x17 then
|
||||
-- dprint2("doesn't look like a SSL record, b0=",b0)
|
||||
return TLS_NOT_TLS, 0
|
||||
end
|
||||
|
||||
local recordLen = 5 + tvb:range(3, 2):uint()
|
||||
if remLen < recordLen then
|
||||
return TLS_NOT_COMPLETE, recordLen
|
||||
end
|
||||
return TLS_OK, recordLen
|
||||
end
|
||||
|
||||
|
||||
function getSslFragments(pktinfo)
|
||||
local addr0 = pktinfo.net_src
|
||||
local addr1 = pktinfo.net_dst
|
||||
local port0 = pktinfo.src_port
|
||||
local port1 = pktinfo.dst_port
|
||||
local key = tostring(addr0) .. ":" .. tostring(port0) .. "->" .. tostring(addr1) .. ":" .. tostring(port1)
|
||||
|
||||
local tableRecord = tableItem(pktinfo)
|
||||
if tableRecord.sslFragments[key] == nil then
|
||||
tableRecord.sslFragments[key] = {}
|
||||
end
|
||||
|
||||
return tableRecord.sslFragments[key]
|
||||
end
|
||||
|
||||
function dissectV2(in_tvbuf, pktinfo, tree)
|
||||
-- dprint2("dissecting in UDP2 mode")
|
||||
local pktlen = in_tvbuf:reported_length_remaining()
|
||||
if pktlen < 7 then
|
||||
dprint2("packet ", pktinfo.number, " too short, len=", pktlen)
|
||||
return
|
||||
end
|
||||
|
||||
local conversation = tableItem(pktinfo)
|
||||
local sourceState = nil
|
||||
local targetState = nil
|
||||
if rdpudp.prefs.track_udp2_peer_states then
|
||||
if tostring(pktinfo.net_dst) == conversation.serverAddr then
|
||||
sourceState = conversation.clientState
|
||||
targetState = conversation.serverState
|
||||
else
|
||||
sourceState = conversation.serverState
|
||||
targetState = conversation.clientState
|
||||
end
|
||||
end
|
||||
|
||||
pktinfo.cols.info = ""
|
||||
local info = "("
|
||||
local tvbuf = unwrapPacket(in_tvbuf)
|
||||
local prefixRange = tvbuf:range(0, 1)
|
||||
local prefix_tree = tree:add(pf_PacketPrefixByte, prefixRange)
|
||||
--
|
||||
local packetType = prefix_tree:add(pf_packetType, prefixRange)
|
||||
|
||||
local flagsRange = tvbuf:range(1,2)
|
||||
local flagsTree = tree:add_le(pf_flags, flagsRange)
|
||||
--
|
||||
flagsTree:add_packet_field(pf_flag_ack, flagsRange, ENC_LITTLE_ENDIAN)
|
||||
flagsTree:add_le(pf_flag_data, flagsRange)
|
||||
flagsTree:add_le(pf_flag_ackvec, flagsRange)
|
||||
flagsTree:add_le(pf_flag_aoa, flagsRange)
|
||||
flagsTree:add_le(pf_flag_overhead, flagsRange)
|
||||
flagsTree:add_le(pf_flag_delayackinfo, flagsRange)
|
||||
|
||||
tree:add_le(pf_logWindow, flagsRange)
|
||||
|
||||
local flags = tvbuf:range(1,2):le_uint()
|
||||
|
||||
local startAt = 3
|
||||
if bit32.band(flags, RDPUDP2_ACK) ~= 0 then
|
||||
-- dprint2("got ACK payload")
|
||||
info = info .. "ACK,"
|
||||
local ackTree = tree:add("Ack")
|
||||
|
||||
ackTree:add_le(pf_AckSeq, tvbuf:range(startAt, 2))
|
||||
ackTree:add_le(pf_AckTs, tvbuf:range(startAt+2, 3))
|
||||
ackTree:add(pf_AckSendTimeGap, tvbuf:range(startAt+5, 1))
|
||||
ackTree:add(pf_ndelayedAcks, tvbuf:range(startAt+6, 1))
|
||||
ackTree:add(pf_delayedTimeScale, tvbuf:range(startAt+6, 1))
|
||||
|
||||
local ackSeq = tvbuf:range(startAt, 2):le_uint()
|
||||
local ackTs = tvbuf:range(startAt+2, 3):le_uint()
|
||||
local nacks = bit32.band(tvbuf:range(startAt+6, 1):le_uint(), 0xf)
|
||||
local delayAckTimeScale = bit32.rshift(bit32.band(tvbuf:range(startAt+6, 1):le_uint(), 0xf0), 4)
|
||||
-- dprint2(pktinfo.number,": nACKs=", nacks, "delayAckTS=", bit32.rshift(delayAckTimeScale, 4))
|
||||
|
||||
if rdpudp.prefs.track_udp2_peer_states then
|
||||
targetState.senderLow = ackSeq
|
||||
end
|
||||
|
||||
startAt = startAt + 7
|
||||
if nacks ~= 0 then
|
||||
local acksItem = ackTree:add(pf_delayedAcks, tvbuf:range(startAt, nacks))
|
||||
local i
|
||||
for i = nacks-1, 0, -1 do
|
||||
local ackDelay = tvbuf:range(startAt+i, 1):le_uint() * bit32.lshift(1, delayAckTimeScale)
|
||||
acksItem:add(pf_delayedAck, tvbuf:range(startAt+i, 1), "seq=0x" .. string.format("%0.4x", ackSeq-i-1) .. " ts=" .. ackTs-ackDelay)
|
||||
end
|
||||
acksItem:add(pf_delayedAck, tvbuf:range(startAt, nacks), "seq=0x" .. string.format("%0.4x", ackSeq) .. " ts=" .. ackTs):set_generated()
|
||||
end
|
||||
startAt = startAt + nacks
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP2_OVERHEAD) ~= 0 then
|
||||
info = info .. "OVERHEAD,"
|
||||
|
||||
tree:add_le(pf_OverHeadSize, tvbuf:range(startAt, 1))
|
||||
startAt = startAt + 1
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP2_DELAYACK) ~= 0 then
|
||||
info = info .. "DELAYEDACK,"
|
||||
|
||||
local delayAckItem = tree:add("DelayAckInfo", tvbuf:range(startAt, 3))
|
||||
delayAckItem:add_le(pf_DelayAckMax, tvbuf:range(startAt, 1))
|
||||
delayAckItem:add_le(pf_DelayAckTimeout, tvbuf:range(startAt+1, 2))
|
||||
startAt = startAt + 3
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP2_AOA) ~= 0 then
|
||||
info = info .. "AOA,"
|
||||
tree:add_le(pf_AckOfAcks, tvbuf:range(startAt, 2))
|
||||
startAt = startAt + 2
|
||||
end
|
||||
|
||||
local dataTree
|
||||
local isDummy = (field_rdpudp2_packetType()() == 0x8)
|
||||
if bit32.band(flags, RDPUDP2_DATA) ~= 0 then
|
||||
if isDummy then
|
||||
info = info .. "DUMMY,"
|
||||
else
|
||||
info = info .. "DATA,"
|
||||
end
|
||||
dataTree = tree:add(isDummy and "Dummy Data" or "Data")
|
||||
dataTree:add_le(pf_DataSeqNumber, tvbuf:range(startAt, 2))
|
||||
startAt = startAt + 2
|
||||
end
|
||||
|
||||
if bit32.band(flags, RDPUDP2_ACKVEC) ~= 0 then
|
||||
-- dprint2("got ACKVEC payload")
|
||||
info = info .. "ACKVEC,"
|
||||
|
||||
local codedAckVecSizeA = tvbuf:range(startAt+2, 1):le_uint()
|
||||
local codedAckVecSize = bit32.band(codedAckVecSizeA, 0x7f)
|
||||
local haveTs = bit32.band(codedAckVecSizeA, 0x80) ~= 0
|
||||
|
||||
local ackVecTree = tree:add("AckVec")
|
||||
ackVecTree:add_le(pf_AckvecBaseSeq, tvbuf:range(startAt, 2))
|
||||
ackVecTree:add(pf_AckvecCodecAckVecSize, tvbuf:range(startAt+2, 1))
|
||||
ackVecTree:add(pf_AckvecHaveTs, tvbuf:range(startAt+2, 1))
|
||||
startAt = startAt + 3
|
||||
if haveTs then
|
||||
ackVecTree:add_le(pf_AckvecTimeStamp, tvbuf:range(startAt, 4))
|
||||
startAt = startAt + 4
|
||||
end
|
||||
local codedAckVector = ackVecTree:add("Vector", tvbuf:range(startAt, codedAckVecSize))
|
||||
local seqNumber = field_rdpudp2_ackvec_base()()
|
||||
for i = 0, codedAckVecSize-1, 1 do
|
||||
local bRange = tvbuf:range(startAt + i, 1)
|
||||
local b = bRange:uint()
|
||||
|
||||
local codedAck = codedAckVector:add(pf_AckvecCodedAck, bRange, b)
|
||||
codedAck:add(pf_AckvecCodedAckMode, bRange)
|
||||
|
||||
local itemString = "";
|
||||
if bit32.band(b, 0x80) == 0 then
|
||||
-- bitmap length mode
|
||||
itemString = string.format("bitmap(0x%0.2x): ", b)
|
||||
local mask = 0x1
|
||||
for j = 0, 7-1 do
|
||||
flag = "!"
|
||||
if bit32.band(b, mask) ~= 0 then
|
||||
flag = ""
|
||||
end
|
||||
|
||||
itemString = itemString .. " " .. flag .. string.format("%0.4x", seqNumber)
|
||||
mask = mask * 2
|
||||
seqNumber = seqNumber + 1
|
||||
end
|
||||
else
|
||||
-- run length mode
|
||||
codedAck:add(pf_AckvecCodedAckRleState, bRange)
|
||||
codedAck:add(pf_AckvecCodedAckRleLen, bRange)
|
||||
|
||||
local rleLen = bit32.band(b, 0x3f)
|
||||
itemString = "rle(len=" .. rleLen .. "): ".. (bit32.band(b, 0x40) and "received" or "lost") ..
|
||||
string.format(" %0.4x -> %0.4x", seqNumber, seqNumber + rleLen)
|
||||
seqNumber = seqNumber + rleLen
|
||||
end
|
||||
|
||||
codedAck:set_text(itemString)
|
||||
|
||||
end
|
||||
|
||||
startAt = startAt + codedAckVecSize
|
||||
end
|
||||
|
||||
if not isDummy and bit32.band(flags, RDPUDP2_DATA) ~= 0 then
|
||||
dataTree:add_le(pf_DataChannelSeqNumber, tvbuf:range(startAt, 2))
|
||||
local payload = tvbuf:range(startAt + 2)
|
||||
local subTvb = payload:tvb("payload")
|
||||
|
||||
local channelSeqId = field_rdpudp2_channelSeqNumber()()
|
||||
local sslFragments = getSslFragments(pktinfo)
|
||||
local workTvb = nil
|
||||
|
||||
local sslRes, recordLen = checkSslRecord(subTvb)
|
||||
if rdpudp.prefs.debug_ssl then
|
||||
dprint2("packet=", pktinfo.number, " channelSeq=", channelSeqId,
|
||||
" dataLen=", subTvb:reported_length_remaining(),
|
||||
" sslRes=", sslResNames[sslRes],
|
||||
" recordLen=", recordLen)
|
||||
end
|
||||
if sslRes == TLS_OK then
|
||||
workTvb = subTvb
|
||||
elseif sslRes == TLS_SHORT or sslRes == TLS_NOT_COMPLETE then
|
||||
if rdpudp.prefs.debug_ssl then
|
||||
dprint2("packet=", pktinfo.number, " recording fragment len=", subTvb:len())
|
||||
end
|
||||
|
||||
local frag = ByteArray.new()
|
||||
frag:append(subTvb:bytes())
|
||||
sslFragments[channelSeqId] = frag
|
||||
|
||||
elseif sslRes == TLS_NOT_TLS then
|
||||
local prevFragment = sslFragments[channelSeqId-1]
|
||||
if rdpudp.prefs.debug_ssl then
|
||||
dprint2("packet=",pktinfo.number," picking channelSeq=", channelSeqId-1, " havePrevFragment=", prevFragment ~= nil and "ok" or "no")
|
||||
end
|
||||
if prevFragment ~= nil then
|
||||
-- dprint2("prevLen=",prevFragment:len(), " subTvbLen=",subTvb:len())
|
||||
local testBytes = prevFragment .. subTvb:bytes()
|
||||
local testTvb = ByteArray.tvb(testBytes, "reassembled fragment")
|
||||
|
||||
sslRes, recordLen = checkSslRecord(testTvb)
|
||||
if rdpudp.prefs.debug_ssl then
|
||||
dprint2("packet=", pktinfo.number,
|
||||
" reassembled len=", testTvb:reported_length_remaining(),
|
||||
" sslRes=", sslResNames[sslRes],
|
||||
" recordLen=", recordLen)
|
||||
end
|
||||
if sslRes == TLS_OK then
|
||||
workTvb = testTvb
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if workTvb ~= nil then
|
||||
repeat
|
||||
if rdpudp.prefs.debug_ssl then
|
||||
dprint2("treating workTvbLen=", workTvb:reported_length_remaining(), " recordLen=",recordLen)
|
||||
end
|
||||
local sslFragment = workTvb:range(0, recordLen):tvb("SSL fragment")
|
||||
sslDissector:call(sslFragment, pktinfo, dataTree)
|
||||
|
||||
workTvb = workTvb:range(recordLen):tvb()
|
||||
sslRes, recordLen = checkSslRecord(workTvb)
|
||||
|
||||
if sslRes == TLS_SHORT or sslRes == TLS_NOT_COMPLETE then
|
||||
if rdpudp.prefs.debug_ssl then
|
||||
dprint2("packet=", pktinfo.number, " recording fragment len=", subTvb:len())
|
||||
end
|
||||
|
||||
local frag = ByteArray.new()
|
||||
frag:append(workTvb:bytes())
|
||||
sslFragments[channelSeqId] = frag
|
||||
end
|
||||
|
||||
until sslRes ~= TLS_OK or workTvb:reported_length_remaining() == 0
|
||||
else
|
||||
dataTree:add_le(pf_Data, payload)
|
||||
end
|
||||
end
|
||||
|
||||
info = string.sub(info, 0, -2) .. ")"
|
||||
pktinfo.cols.info = info -- .. tostring(pktinfo.cols.info)
|
||||
if rdpudp.prefs.track_udp2_peer_states then
|
||||
local stateTrackItem = tree:add("UDP2 state tracking")
|
||||
stateTrackItem:set_generated()
|
||||
stateTrackItem:add(tostring(pktinfo.net_dst) == conversation.serverAddr and "Client -> Server" or "Server -> Client")
|
||||
end
|
||||
|
||||
|
||||
return pktlen
|
||||
end
|
||||
|
||||
function rdpudp.dissector(in_tvbuf, pktinfo, root)
|
||||
-- dprint2("rdpudp.dissector called")
|
||||
pktinfo.cols.protocol:set("RDP-UDP")
|
||||
|
||||
local pktlen = in_tvbuf:reported_length_remaining()
|
||||
local tree = root:add(rdpudp, in_tvbuf:range(0,pktlen))
|
||||
|
||||
local tableRecord = tableItem(pktinfo)
|
||||
local doDissectV1 = true
|
||||
if tableRecord.switchToUdp2 ~= nil and tableRecord.switchToUdp2 < pktinfo.number then
|
||||
doDissectV1 = false
|
||||
end
|
||||
|
||||
if doDissectV1 then
|
||||
return dissectV1(in_tvbuf, pktinfo, tree)
|
||||
end
|
||||
|
||||
return dissectV2(in_tvbuf, pktinfo, tree)
|
||||
end
|
||||
|
||||
DissectorTable.get("udp.port"):add(3389, rdpudp)
|
||||
Reference in New Issue
Block a user