Files
Rafael Rivera 5b090917d7 Initial commit
2019-11-18 16:46:08 -08:00

537 lines
18 KiB
Plaintext

// A common header for all Samba syslog messages. Other messages inherit from the header, add more fields and override mutiline log parsing
message samba_header with
//Regex: Pass QE Check 8/1/2013
EntryInfo { Regex = @"\[(?<ts>[ \d:\./]+),\s*(?<level>\d+)\]\s*(?<source_file>[- \w/_\d\.]+)\:(?<file_line>\d+)\((?<function>.*)\)", Multiline = parseContent } : LogEntry
{
DateTime ts with EntryFieldInfo { IsTimestamp = true, IsLocalTime = true };
ushort level;
string source_file;
uint file_line;
string function;
optional string content;
bool parseContent(string line, LineContext context)
{
if (context.IsInitialLine) return true;
// If a header cannot be decoded then we assume that it is some description text,
// which should go to the content property
if (CanDecode<samba_header>(line, context)) return false;
if (content == nothing)
{
content = line.Trim();
}
else
{
// Cannot attach text as multiple lines because UI shows only the first line in that case
content = (content as string) + " " + line.Trim();
}
return true;
}
}
// ------ SMB Command parsing ----- //
// Represents a logged Samba command. The type also provides a helper builder method for
// iterative parsing of the command fields.
type smbcmd
{
optional uint size;
optional uint smb_com;
optional uint smb_com2;
optional int smb_rcls;
optional int smb_reh;
optional int smb_err;
optional int smb_flg;
optional int smb_flg2;
optional uint smb_tid;
optional uint smb_pid;
optional uint smb_uid;
optional uint smb_mid;
optional uint smt_wct;
optional array<int> smb_vwv;
optional int smb_bcc;
// Parses name-value pairs into the command fields. Attaches a validation error to the specified
// message if a given name-value pair is unknown.
bool parse(string line, LineContext context, any message m)
{
line = line.Trim();
var nvp = Decode<name_value_pair>(line, context);
// Done parsing if the specified line is not in the name-value pair format
if (nvp == null) return false;
// Switch on the name and initialized a corresponding field with a parsed value
switch (nvp.n)
{
case "size" => size = (nvp.v as uint);
case "smb_com" => smb_com = (nvp.v as uint);
case "smb_com2" => smb_com = (nvp.v as uint);
case "smb_rcls" => smb_rcls = (nvp.v as int);
case "smb_reh" => smb_reh = (nvp.v as int);
case "smb_err" => smb_err = (nvp.v as int);
case "smb_flg" => smb_flg = (nvp.v as int);
case "smb_flg2" => smb_flg2 = (nvp.v as int);
case "smb_tid" => smb_tid = (nvp.v as uint);
case "smb_pid" => smb_pid = (nvp.v as uint);
case "smb_uid" => smb_uid = (nvp.v as uint);
case "smb_mid" => smb_mid = (nvp.v as uint);
case "smt_wct" => smt_wct = (nvp.v as uint);
case "smb_vwv" =>
{
if (smb_vwv == nothing) smb_vwv = [];
var smb_vwv_v = smb_vwv as array<int>;
smb_vwv_v += [(nvp.v as int)];
smb_vwv = smb_vwv_v;
}
case "smb_bcc" => smb_bcc = (nvp.v as int);
default => ValidationCheck(false, m, 2 /* warning */, "Unknown smb_command flag: " + nvp.n);
}
return true;
}
}
// Parses name-value pairs in the following forms:
// 1. name=value
// 2. name[ 0]= value (0x10)
message name_value_pair with
//Regex: Not Pass QE Check 9/10/2013 Used lazy quantifiers(as-few-times-as-possible quantifiers) which SDL Regex Fuzzer does not support.
//Looks like the regular expression is safe.
EntryInfo { Regex = @"^(?<n>[\w\d_]+)(\[.*?\])?=\s*(?<v>[\d\w]+)" } : LogEntry
{
string n;
string v;
}
message smb_command with
//Regex: Pass QE Check 9/10/2013
EntryInfo { Tag = "show_msg", Regex = @" ?", Multiline = parseFields, Priority = 0 } : samba_header
{
optional smbcmd command;
bool parseFields(string line, LineContext context)
{
if (!context.IsInitialLine)
{
var cmd = command == nothing ? new smbcmd() : command as smbcmd;
if (!cmd.parse(line, context, this))
{
return false;
}
command = cmd;
}
return true;
}
}
// ------ NTLMSSP Negotiation parsing ----- //
message ntlmssp_negotiation with
//Regex: Pass QE Check 9/10/2013
EntryInfo { Tag = "debug_ntlmssp_flags", Regex = @" ?", Multiline = parseFlags, Priority = 0 } : samba_header
{
ulong neg_flags with DisplayInfo{ToText = ToHex};
optional bool NTLMSSP_NEGOTIATE_UNICODE;
optional bool NTLMSSP_NEGOTIATE_OEM;
optional bool NTLMSSP_REQUEST_TARGET;
optional bool NTLMSSP_NEGOTIATE_SIGN;
optional bool NTLMSSP_NEGOTIATE_SEAL;
optional bool NTLMSSP_NEGOTIATE_DATAGRAM;
optional bool NTLMSSP_NEGOTIATE_LM_KEY;
optional bool NTLMSSP_NEGOTIATE_NETWARE;
optional bool NTLMSSP_NEGOTIATE_NTLM;
optional bool NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;
optional bool NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;
optional bool NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL;
optional bool NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
optional bool NTLMSSP_REQUEST_NON_NT_SESSION_KEY;
optional bool NTLMSSP_NEGOTIATE_NTLM2;
optional bool NTLMSSP_NEGOTIATE_TARGET_INFO;
optional bool NTLMSSP_NEGOTIATE_VERSION;
optional bool NTLMSSP_NEGOTIATE_128;
optional bool NTLMSSP_NEGOTIATE_KEY_EXCH;
optional bool NTLMSSP_NEGOTIATE_56;
bool parseFlags(string line, LineContext context)
{
if (!context.IsInitialLine)
{
// We assume that the first line after the initial line has a format similar to:
// Got NTLMSSP neg_flags=0xe2088215
if (!("neg_flags" in context.Data))
{
var flags_index = line.LastIndexOf("=");
if (flags_index != -1)
{
neg_flags = line.Segment(flags_index + "=".Count).Trim() as ulong;
context.Data["neg_flags"] = true;
return true;
}
else
{
// Something is not as expected - terminate further parsing
return false;
}
}
var flag_name = line.Trim();
switch (flag_name)
{
case "NTLMSSP_NEGOTIATE_UNICODE" => NTLMSSP_NEGOTIATE_UNICODE = true;
case "NTLMSSP_NEGOTIATE_OEM" => NTLMSSP_NEGOTIATE_OEM = true;
case "NTLMSSP_REQUEST_TARGET" => NTLMSSP_REQUEST_TARGET = true;
case "NTLMSSP_NEGOTIATE_SIGN" => NTLMSSP_NEGOTIATE_SIGN = true;
case "NTLMSSP_NEGOTIATE_SEAL" => NTLMSSP_NEGOTIATE_SEAL = true;
case "NTLMSSP_NEGOTIATE_DATAGRAM" => NTLMSSP_NEGOTIATE_DATAGRAM = true;
case "NTLMSSP_NEGOTIATE_LM_KEY" => NTLMSSP_NEGOTIATE_LM_KEY = true;
case "NTLMSSP_NEGOTIATE_NETWARE" => NTLMSSP_NEGOTIATE_NETWARE = true;
case "NTLMSSP_NEGOTIATE_NTLM" => NTLMSSP_NEGOTIATE_NTLM = true;
case "NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED" => NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = true;
case "NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED" => NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = true;
case "NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL" => NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL = true;
case "NTLMSSP_NEGOTIATE_ALWAYS_SIGN" => NTLMSSP_NEGOTIATE_ALWAYS_SIGN = true;
case "NTLMSSP_REQUEST_NON_NT_SESSION_KEY" => NTLMSSP_REQUEST_NON_NT_SESSION_KEY = true;
case "NTLMSSP_NEGOTIATE_NTLM2" => NTLMSSP_NEGOTIATE_NTLM2 = true;
case "NTLMSSP_NEGOTIATE_TARGET_INFO" => NTLMSSP_NEGOTIATE_TARGET_INFO = true;
case "NTLMSSP_NEGOTIATE_VERSION" => NTLMSSP_NEGOTIATE_VERSION = true;
case "NTLMSSP_NEGOTIATE_128" => NTLMSSP_NEGOTIATE_128 = true;
case "NTLMSSP_NEGOTIATE_KEY_EXCH" => NTLMSSP_NEGOTIATE_KEY_EXCH = true;
case "NTLMSSP_NEGOTIATE_56" => NTLMSSP_NEGOTIATE_56 = true;
// Terminate parsing if we encountered some unknown value
default => return false;
}
}
return true;
}
}
// ------ Security Token parsing ----- //
message security_token with
//Regex: Pass QE Check 9/10/2013
EntryInfo { Tag = "security_token_debug", Regex = @" ?", Multiline = parseSecurity, Priority = 0 } : samba_header
{
optional array<string> sids;
optional ulong privileges_mask;
optional array<string> privileges;
optional ulong rights_mask;
optional array<string> rights;
bool parseSecurity(string line, LineContext context)
{
if (!context.IsInitialLine)
{
var trimmed = line.Trim();
if (trimmed.IndexOf("Security token") == 0)
{
// Security token line does not carry any additional information, so just skip it
}
else if (trimmed.IndexOf("SID") == 0)
{
parseSID(trimmed);
}
else if (trimmed.IndexOf("Privileges") == 0)
{
parsePrivilegesMask(trimmed);
}
else if (trimmed.IndexOf("Privilege") == 0)
{
parsePrivilege(trimmed);
}
else if (trimmed.IndexOf("Rights") == 0)
{
parseRightsMask(trimmed);
}
else if (trimmed.IndexOf("Right") == 0)
{
parseRight(trimmed);
}
else
{
return false;
}
}
return true;
}
void parseSID(string line)
{
// The line of the following format is expected:
// SID[ 0]: S-1-5-21-695135575-2411905282-2776759044-1008
var sidIndex = line.LastIndexOf(":");
if (sidIndex != -1)
{
var sid = line.Segment(sidIndex + 1).Trim();
if (sids == nothing) sids = [];
var sids_v = sids as array<string>;
sids_v += [sid];
sids = sids_v;
}
else
{
ValidationCheck(false, this, 2 /* warning */, "SID has invalid format: " + line);
}
}
void parsePrivilegesMask(string line)
{
// The line of the following format is expected:
// Privileges (0x 1FFFFFF00):
var mask = parseMaskHeader(line, this, "Privileges");
if (mask != nothing) privileges_mask = mask;
}
void parsePrivilege(string line)
{
// The line of the following format is expected:
// Privilege[ 0]: SeTakeOwnershipPrivilege
var privilege = parseSecuritySetting(line, this, "Privilege");
if (privilege != null)
{
if (privileges == nothing) privileges = [];
var privileges_v = privileges as array<string>;
privileges_v += [privilege];
privileges = privileges_v;
}
}
void parseRightsMask(string line)
{
// The line of the following format is expected:
// Rights (0x 403):
var mask = parseMaskHeader(line, this, "Rights");
if (mask != nothing) rights_mask = mask;
}
void parseRight(string line)
{
// The line of the following format is expected:
// Right[ 0]: SeInteractiveLogonRight
var right = parseSecuritySetting(line, this, "Right");
if (right != null)
{
if (rights == nothing) rights = [];
var rights_v = rights as array<string>;
rights_v += [right];
rights = rights_v;
}
}
// Parses a mask header. The method attaches a validation error
// to a message if the specified payload cannot be parsed.
// Return: a parsed mask value; or nothing if the payload could not be parsed.
static optional ulong parseMaskHeader(string payload, any message m, string header)
{
var maskStartIndex = payload.IndexOf("0x");
var maskEndIndex = payload.LastIndexOf(")");
if (maskStartIndex != -1 && maskEndIndex != -1)
{
maskStartIndex += "0x".Count;
var mask = payload.Segment(maskStartIndex, maskEndIndex - maskStartIndex).Trim();
mask = "0x" + mask;
return (mask as ulong);
}
else
{
ValidationCheck(false, m, 2 /* warning */, header + " header has invalid format: " + payload);
return nothing;
}
}
// Parses a security setting line. The method attaches a validation error
// to a message if the specified payload cannot be parsed.
// Return: a parsed setting value; or null if the payload could not be parsed.
static string parseSecuritySetting(string payload, any message m, string header)
{
var setting = null;
var settingIndex = payload.LastIndexOf(":");
if (settingIndex != -1)
{
setting = payload.Segment(settingIndex + 1).Trim();
}
else
{
ValidationCheck(false, m, 2 /* warning */, header + " has invalid format: " + payload);
}
return setting;
}
}
// ------ SMB Set Time parsing ----- //
message smb_set_file_time with
//Regex: Pass QE Check 9/10/2013
EntryInfo { Tag = "smb_set_file_time", Regex = @" ?", Multiline = parseSetTime, Priority = 0 } : samba_header
{
string actime;
string modtime;
string ctime;
string createtime;
optional smbcmd command;
optional string text;
bool parseSetTime(string line, LineContext context)
{
if (context.IsInitialLine)
{
return true;
}
var trimmed = line.Trim();
// Skip empty lines
if (trimmed.Count == 0) return true;
// First try to parse time values
if (parseTime(trimmed)) return true;
if (command == nothing && text == nothing)
{
var cmd = new smbcmd();
if (cmd.parse(trimmed, context, this))
{
command = cmd;
return true;
}
// If the line cannot be parsed as samba_header then we assume that it belongs to
// the current smb_set_file_time message
if (!CanDecode<samba_header>(line, context))
{
text = trimmed;
return true;
}
}
else
{
if (command != nothing)
{
return (command as smbcmd).parse(trimmed, context, this);
}
}
return false;
}
// Parse set-time command time values. Return true if the parsing is successfull; otherwise false.
bool parseTime(string line)
{
if (actime == null)
{
var index = line.IndexOf(actimePrefix);
if (index != -1)
{
var time = line.Segment(index + actimePrefix.Count).Trim();
actime = time;
return true;
}
}
if (modtime == null)
{
var index = line.IndexOf(modtimePrefix);
if (index != -1)
{
var time = line.Segment(index + modtimePrefix.Count).Trim();
modtime = time;
return true;
}
}
if (ctime == null)
{
var index = line.IndexOf(ctimePrefix);
if (index != -1)
{
var time = line.Segment(index + ctimePrefix.Count).Trim();
ctime = time;
return true;
}
}
if (createtime == null)
{
var index = line.IndexOf(createtimePrefix);
if (index != -1)
{
var time = line.Segment(index + createtimePrefix.Count).Trim();
createtime = time;
return true;
}
}
return false;
}
}
// Cannot declare those fields within a message because UI still shows them
const string actimePrefix = "actime:";
const string modtimePrefix = "modtime:";
const string ctimePrefix = "ctime:";
const string createtimePrefix = "createtime:";
string ToHex(any data)
{
string text = "Cannot convert to hex";
ulong v = 0;
if (data is uint || data is ulong)
{
v = data as ulong;
text = null;
}
// Currently is-operator is not supported for optional types
//else if (data is optional ulong)
//{
// var o = data as optional ulong;
// if (o == nothing)
// {
// text = "nothing";
// }
// else
// {
// v = o;
// text = null;
// }
//}
return text == null ? UlongToHex(v) : text;
}
string UlongToHex(ulong val)
{
var text = "";
while (val >= 16)
{
var remainder = val % 16;
text = ToHexDigit(remainder) + text;
val = val >> 4;
}
text = "0x" + ToHexDigit(val) + text;
return text;
}
string ToHexDigit(ulong val)
{
if (val >= 16) return null;
if (val < 10) return val as string;
if (val == 10) return "A";
if (val == 11) return "B";
if (val == 12) return "C";
if (val == 13) return "D";
if (val == 14) return "E";
return "F";
}