1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Trapdirector; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
|
7
|
|
|
class Rule |
8
|
|
|
{ |
9
|
|
|
use \RuleUtils; |
10
|
|
|
|
11
|
|
|
/** @var Logging $logging logging class*/ |
12
|
|
|
protected $logging; |
13
|
|
|
|
14
|
|
|
/** @var Trap $trapClass */ |
15
|
|
|
protected $trapClass; |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Setup Rule Class |
20
|
|
|
* @param Trap $trapClass : To get logging class & plugin class |
21
|
|
|
*/ |
22
|
|
|
function __construct($trapClass) |
23
|
|
|
{ |
24
|
|
|
$this->trapClass=$trapClass; |
25
|
|
|
$this->logging=$trapClass->logging; |
26
|
|
|
} |
27
|
|
|
|
28
|
|
|
|
29
|
|
|
protected function eval_getElement($rule,&$item) |
30
|
|
|
{ |
31
|
|
|
if ($item >= strlen($rule)) |
32
|
|
|
{ |
33
|
|
|
throw new Exception("Early end of string ".$rule ." at " .$item ); |
34
|
|
|
} |
35
|
|
|
while ($rule[$item]==' ') $item++; |
36
|
|
|
if (preg_match('/[\-0-9\.]/',$rule[$item])) |
37
|
|
|
{ // number |
38
|
|
|
return $this->get_number($rule, $item); |
39
|
|
|
} |
40
|
|
|
if ($rule[$item] == '"') |
41
|
|
|
{ // string |
42
|
|
|
return $this->get_string($rule, $item); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
if ($rule[$item] == '(') |
46
|
|
|
{ // grouping |
47
|
|
|
return $this->get_group($rule, $item); |
48
|
|
|
} |
49
|
|
|
if ($rule[$item] == '_') |
50
|
|
|
{ // function |
51
|
|
|
return $this->get_function($rule, $item); |
52
|
|
|
} |
53
|
|
|
throw new Exception("number/string not found in ".$rule ." at " .$item . ' : ' .$rule[$item]); |
54
|
|
|
|
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
protected function eval_getOper($rule,&$item) |
58
|
|
|
{ |
59
|
|
|
while ($rule[$item]==' ') $item++; |
60
|
|
|
switch ($rule[$item]) |
61
|
|
|
{ |
62
|
|
|
case '<': |
63
|
|
|
if ($rule[$item+1]=='=') { $item+=2; return array(0,"<=");} |
64
|
|
|
$item++; return array(0,"<"); |
65
|
|
|
case '>': |
66
|
|
|
if ($rule[$item+1]=='=') { $item+=2; return array(0,">=");} |
67
|
|
|
$item++; return array(0,">"); |
68
|
|
|
case '=': |
69
|
|
|
$item++; return array(0,"="); |
70
|
|
|
case '!': |
71
|
|
|
if ($rule[$item+1]=='=') { $item+=2; return array(0,"!=");} |
72
|
|
|
throw new Exception("Erreur in expr - incorrect operator '!' found in ".$rule ." at " .$item); |
73
|
|
|
case '~': |
74
|
|
|
$item++; return array(0,"~"); |
75
|
|
|
case '|': |
76
|
|
|
$item++; return array(1,"|"); |
77
|
|
|
case '&': |
78
|
|
|
$item++; return array(1,"&"); |
79
|
|
|
default : |
80
|
|
|
throw new Exception("Erreur in expr - operator not found in ".$rule ." at " .$item); |
81
|
|
|
} |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
private function do_compare($val1,$val2,$comp,$negate) |
85
|
|
|
{ |
86
|
|
|
switch ($comp){ |
87
|
|
|
case '<': $retVal= ($val1 < $val2); break; |
88
|
|
|
case '<=': $retVal= ($val1 <= $val2); break; |
89
|
|
|
case '>': $retVal= ($val1 > $val2); break; |
90
|
|
|
case '>=': $retVal= ($val1 >= $val2); break; |
91
|
|
|
case '=': $retVal= ($val1 == $val2); break; |
92
|
|
|
case '!=': $retVal= ($val1 != $val2); break; |
93
|
|
|
case '~': $retVal= (preg_match('/'.preg_replace('/"/','',$val2).'/',$val1)); break; |
94
|
|
|
case '|': $retVal= ($val1 || $val2); break; |
95
|
|
|
case '&': $retVal= ($val1 && $val2); break; |
96
|
|
|
default: throw new Exception("Error in expression - unknown comp : ".$comp); |
97
|
|
|
} |
98
|
|
|
if ($negate === true) $retVal = ! $retVal; // Inverse result if negate before expression |
99
|
|
|
|
100
|
|
|
return $retVal; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** Evaluation : makes token and evaluate. |
104
|
|
|
* Public function for expressions testing |
105
|
|
|
* accepts : < > = <= >= != (typec = 0) |
106
|
|
|
* operators : & | (typec=1) |
107
|
|
|
* with : integers/float (type 0) or strings "" (type 1) or results (type 2) |
108
|
|
|
* comparison int vs strings will return null (error) |
109
|
|
|
* return : bool or null on error |
110
|
|
|
*/ |
111
|
|
|
public function evaluation($rule,&$item) |
112
|
|
|
{ |
113
|
|
|
//echo "Evaluation of ".substr($rule,$item)."\n"; |
114
|
|
|
$negate=$this->check_negate_first($rule, $item); |
115
|
|
|
// First element : number, string or () |
116
|
|
|
list($type1,$val1) = $this->eval_getElement($rule,$item); |
117
|
|
|
//echo "Elmt1: ".$val1."/".$type1." : ".substr($rule,$item)."\n"; |
118
|
|
|
|
119
|
|
|
if ($item==strlen($rule)) // If only element, return value, but only boolean |
120
|
|
|
{ |
121
|
|
|
if ($type1 != 2) throw new Exception("Cannot use num/string as boolean : ".$rule); |
122
|
|
|
if ($negate === true) $val1= ! $val1; |
123
|
|
|
return $val1; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
// Second element : operator |
127
|
|
|
list($typec,$comp) = $this->eval_getOper($rule,$item); |
128
|
|
|
//echo "Comp : ".$comp." : ".substr($rule,$item)."\n"; |
129
|
|
|
|
130
|
|
|
// Third element : number, string or () |
131
|
|
|
if ( $rule[$item] == '!') // starts with a ! so evaluate whats next |
132
|
|
|
{ |
133
|
|
|
$item++; |
134
|
|
|
if ($typec != 1) throw new Exception("Mixing boolean and comparison : ".$rule); |
135
|
|
|
$val2= ! $this->evaluation($rule,$item); |
136
|
|
|
$type2=2; // result is a boolean |
137
|
|
|
} |
138
|
|
|
else |
139
|
|
|
{ |
140
|
|
|
list($type2,$val2) = $this->eval_getElement($rule,$item); |
141
|
|
|
} |
142
|
|
|
//echo "Elmt2: ".$val2."/".$type2." : ".substr($rule,$item)."\n"; |
143
|
|
|
|
144
|
|
|
if ($type1!=$type2) // cannot compare different types |
145
|
|
|
{ |
146
|
|
|
throw new Exception("Cannot compare string & number : ".$rule); |
147
|
|
|
} |
148
|
|
|
if ($typec==1 && $type1 !=2) // cannot use & or | with string/number |
149
|
|
|
{ |
150
|
|
|
throw new Exception("Cannot use boolean operators with string & number : ".$rule); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
$retVal = $this->do_compare($val1, $val2, $comp, $negate); |
154
|
|
|
|
155
|
|
|
if ($item==strlen($rule)) return $retVal; // End of string : return evaluation |
156
|
|
|
// check for logical operator : |
157
|
|
|
switch ($rule[$item]) |
158
|
|
|
{ |
159
|
|
|
case '|': $item++; return ($retVal || $this->evaluation($rule,$item) ); |
160
|
|
|
case '&': $item++; return ($retVal && $this->evaluation($rule,$item) ); |
161
|
|
|
|
162
|
|
|
default: throw new Exception("Erreur in expr - garbadge at end of expression : ".$rule[$item]); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Get '*' or '**' and transform in [0-9]+ or .* in return string |
168
|
|
|
* @param string $oid OID in normal or regexp format. '*' will be escaped ('\*') |
169
|
|
|
* @return string correct regexp format |
170
|
|
|
*/ |
171
|
|
|
public function regexp_eval(string &$oid) |
172
|
|
|
{ |
173
|
|
|
// ** replaced by .* |
174
|
|
|
$oidR=preg_replace('/\*\*/', '.*', $oid); |
175
|
|
|
// * replaced by [0-9]+ |
176
|
|
|
$oidR=preg_replace('/\*/', '[0-9]+', $oidR); |
177
|
|
|
|
178
|
|
|
// replace * with \* in oid for preg_replace |
179
|
|
|
$oid=preg_replace('/\*/', '\*', $oid); |
180
|
|
|
|
181
|
|
|
$this->logging->log('Regexp eval : '.$oid.' / '.$oidR,DEBUG ); |
182
|
|
|
|
183
|
|
|
return $oidR; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
|
187
|
|
|
/** Evaluation rule (uses eval_* functions recursively) |
188
|
|
|
* @param string $rule : rule ( _OID(.1.3.6.1.4.1.8072.2.3.2.1)=_OID(.1.3.6.1.2.1.1.3.0) ) |
189
|
|
|
* @param array $oidList : OIDs values to sustitute. |
190
|
|
|
* @return bool : true : rule match, false : rule don't match , throw exception on error. |
191
|
|
|
*/ |
192
|
|
|
public function eval_rule($rule,$oidList) |
193
|
|
|
{ |
194
|
|
|
if ($rule==null || $rule == '') // Empty rule is always true |
195
|
|
|
{ |
196
|
|
|
return true; |
197
|
|
|
} |
198
|
|
|
$matches=array(); |
199
|
|
|
while (preg_match('/_OID\(([0-9\.\*]+)\)/',$rule,$matches) == 1) |
200
|
|
|
{ |
201
|
|
|
$oid=$matches[1]; |
202
|
|
|
$found=0; |
203
|
|
|
// Test and transform regexp |
204
|
|
|
$oidR = $this->regexp_eval($oid); |
205
|
|
|
|
206
|
|
|
foreach($oidList as $val) |
207
|
|
|
{ |
208
|
|
|
if (preg_match("/^$oidR$/",$val->oid) == 1) |
209
|
|
|
{ |
210
|
|
|
if (!preg_match('/^-?[0-9]*\.?[0-9]+$/',$val->value)) |
211
|
|
|
{ // If not a number, change " to ' and put " around it |
212
|
|
|
$val->value=preg_replace('/"/',"'",$val->value); |
213
|
|
|
$val->value='"'.$val->value.'"'; |
214
|
|
|
} |
215
|
|
|
$rep=0; |
216
|
|
|
$rule=preg_replace('/_OID\('.$oid.'\)/',$val->value,$rule,-1,$rep); |
217
|
|
|
if ($rep==0) |
218
|
|
|
{ |
219
|
|
|
$this->logging->log("Error in rule_eval",WARN,''); |
220
|
|
|
return false; |
221
|
|
|
} |
222
|
|
|
$found=1; |
223
|
|
|
break; |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
if ($found==0) |
227
|
|
|
{ // OID not found : throw error |
228
|
|
|
throw new Exception('OID '.$oid.' not found in trap'); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
$item=0; |
232
|
|
|
$rule=$this->eval_cleanup($rule); |
233
|
|
|
$this->logging->log('Rule after clenup: '.$rule,INFO ); |
234
|
|
|
|
235
|
|
|
return $this->evaluation($rule,$item); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
} |