api.cobainsshotgun.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. /**
  3. * Performs radius.log analyze to detect successful and failed auth attempts
  4. */
  5. class CobainsShotgun {
  6. /**
  7. * Pre-defines Routes
  8. */
  9. const URL_ME = '?module=cobainsshotgun';
  10. const ROUTE_ZEN = 'zenmode';
  11. /**
  12. * Contains default raw-data source path.
  13. * May be configurable in future.
  14. *
  15. * @var string
  16. */
  17. protected $dataSource = '/var/log/radius.log';
  18. /**
  19. * Contains auth data text marker to extract from datasource
  20. *
  21. * @var string
  22. */
  23. protected $authDataMask = 'Auth:';
  24. /**
  25. * Mask of successful auth in log
  26. *
  27. * @var string
  28. */
  29. protected $authOkMask = 'Login OK:';
  30. /**
  31. * System billing.ini config as key=>value
  32. *
  33. * @var object
  34. */
  35. protected $billCfg = array();
  36. /**
  37. * System messages object placeholder
  38. *
  39. * @var object
  40. */
  41. protected $messages = '';
  42. public function __construct() {
  43. $this->loadConfigs();
  44. $this->initMessages();
  45. }
  46. /**
  47. * Preloads required system configs for further usage
  48. *
  49. * @global object $ubillingConfig
  50. *
  51. * @return void
  52. */
  53. protected function loadConfigs() {
  54. global $ubillingConfig;
  55. $this->billCfg = $ubillingConfig->getBilling();
  56. }
  57. /**
  58. * Inits system messages helper
  59. *
  60. * @return void
  61. */
  62. protected function initMessages() {
  63. $this->messages = new UbillingMessageHelper();
  64. }
  65. /**
  66. * Returns array of filtered auth strings from datasource
  67. *
  68. * @return array
  69. */
  70. protected function getRawData() {
  71. $result = array();
  72. $command = $this->billCfg['SUDO'] . ' ' . $this->billCfg['CAT'] . ' ' . $this->dataSource . ' | ' . $this->billCfg['GREP'] . ' "' . $this->authDataMask . '"';
  73. $result = shell_exec($command);
  74. if (!empty($result)) {
  75. $result = explodeRows($result);
  76. }
  77. return($result);
  78. }
  79. /**
  80. * Extracts username from square brackets
  81. *
  82. * @param string $string
  83. *
  84. * @return string
  85. */
  86. public function extractUserName($string) {
  87. $result = '';
  88. if (preg_match('!\[(.*?)\]!si', $string, $tmpArr)) {
  89. $result = $tmpArr[1];
  90. }
  91. return($result);
  92. }
  93. /**
  94. * Renders module controls panel
  95. *
  96. * @return string
  97. */
  98. public function renderControls() {
  99. $result = '';
  100. $result .= wf_Link(self::URL_ME, wf_img('skins/icon_shotgun.png', __('Shotgun')) . ' ' . __('Shotgun'), false, 'ubButton') . ' ';
  101. $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_ZEN . '=true', wf_img('skins/zen.png', __('Zen')) . ' ' . __('Zen'), false, 'ubButton') . ' ';
  102. return ($result);
  103. }
  104. /**
  105. * Renders CobainsShotgun UI for ZenFlow
  106. *
  107. * @param $linesToRead
  108. * @return string
  109. */
  110. public function renderReportZen($linesToRead = 200) {
  111. // Variable that stores UI elements.
  112. $result = '';
  113. // Check if file exists.
  114. if (!file_exists($this->dataSource)) {
  115. $messages = new UbillingMessageHelper();
  116. $result .= $messages->getStyledMessage(__('File not exist') . ': ' . $this->dataSource, 'error');
  117. return $result;
  118. }
  119. // Get data from 'radius.log'.
  120. $command = $this->billCfg['SUDO'] . ' ' . $this->billCfg['CAT'] . ' ' . $this->dataSource . ' | '
  121. . $this->billCfg['GREP'] . ' "' . $this->authDataMask . '" ' . ' | '
  122. . $this->billCfg['TAIL'] . ' -r ' . ' -n ' . $linesToRead;
  123. $cmdResult = shell_exec($command);
  124. // Check if we have any data.
  125. if (empty($cmdResult)) {
  126. $messages = new UbillingMessageHelper();
  127. $result .= $messages->getStyledMessage(__('Nothing to show'), 'warning');
  128. return $result;
  129. }
  130. $cmdResult = explodeRows($cmdResult);
  131. $rows = '';
  132. // Transform received data into Table-like UI element
  133. foreach ($cmdResult as $singleRow) {
  134. $cells = wf_TableCell(htmlentities(strip_tags($singleRow)));
  135. $rows .= wf_TableRow($cells, 'row5');
  136. }
  137. $result .= wf_TableBody($rows, '100%', 0, '', 'style="font-family: monospace;"');
  138. return ($result);
  139. }
  140. /**
  141. * Render the report. What did you expect?
  142. *
  143. * @return string
  144. */
  145. public function renderReport() {
  146. $result = '';
  147. $rawData = $this->getRawData();
  148. $usernameCounters = array();
  149. if (!empty($rawData)) {
  150. foreach ($rawData as $io => $eachLine) {
  151. if (!empty($eachLine)) {
  152. $userName = $this->extractUserName($eachLine);
  153. $userMac = zb_ExtractMacAddress($eachLine);
  154. if (empty($userName)) {
  155. $userName = __('Empty') . ' / ' . $userMac;
  156. }
  157. if (!empty($userName)) {
  158. //prefill attempts counters
  159. if (!isset($usernameCounters[$userName])) {
  160. $usernameCounters[$userName]['ok'] = 0;
  161. $usernameCounters[$userName]['fail'] = 0;
  162. $usernameCounters[$userName]['mac'] = $userMac;
  163. }
  164. //counting attempts for each username
  165. if (ispos($eachLine, $this->authOkMask)) {
  166. $usernameCounters[$userName]['ok'] ++;
  167. } else {
  168. $usernameCounters[$userName]['fail'] ++;
  169. }
  170. }
  171. }
  172. }
  173. if (!empty($usernameCounters)) {
  174. $cells = wf_TableCell(__('Username') . ' ' . __('Radius'));
  175. $cells .= wf_TableCell(__('MAC'));
  176. $cells .= wf_TableCell(__('Success'));
  177. $cells .= wf_TableCell(__('Failed'));
  178. $cells .= wf_TableCell(__('Total'));
  179. $rows = wf_TableRow($cells, 'row1');
  180. foreach ($usernameCounters as $eachUserName => $eachStats) {
  181. $cells = wf_TableCell($eachUserName);
  182. $cells .= wf_TableCell($eachStats['mac']);
  183. $cells .= wf_TableCell($eachStats['ok']);
  184. $cells .= wf_TableCell($eachStats['fail']);
  185. $cells .= wf_TableCell($eachStats['ok'] + $eachStats['fail']);
  186. $rows .= wf_TableRow($cells, 'row5');
  187. }
  188. $result .= wf_TableBody($rows, '100%', 0, 'sortable');
  189. }
  190. } else {
  191. $result .= $this->messages->getStyledMessage(__('Nothing to show'), 'warning');
  192. }
  193. return($result);
  194. }
  195. }