radius.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. <?php
  2. require_once "Net/CheckIP.php";
  3. /**
  4. * RadAct
  5. *
  6. */
  7. class RadAct{
  8. /** @var String */
  9. public $radacctid;
  10. /** @var String */
  11. public $acctsessionid;
  12. /** @var String */
  13. public $acctuniqueid;
  14. /** @var String */
  15. public $username;
  16. /** @var String */
  17. public $groupname;
  18. /** @var String */
  19. public $realm;
  20. /** @var String */
  21. public $nasipaddress;
  22. /** @var String */
  23. public $nasportid;
  24. /** @var String */
  25. public $nasporttype;
  26. /** @var String */
  27. public $acctstarttime;
  28. /** @var String */
  29. public $acctstoptime;
  30. /** @var String */
  31. public $acctsessiontime;
  32. /** @var String */
  33. public $acctauthentic;
  34. /** @var String */
  35. public $connectinfo_start;
  36. /** @var String */
  37. public $connectinfo_stop;
  38. /** @var String */
  39. public $acctinputoctets;
  40. /** @var String */
  41. public $acctoutputoctets;
  42. /** @var String */
  43. public $calledstationid;
  44. /** @var String */
  45. public $callingstationid;
  46. /** @var String */
  47. public $acctterminatecause;
  48. /** @var String */
  49. public $servicetype;
  50. /** @var String */
  51. public $framedprotocol;
  52. /** @var String */
  53. public $framedipaddress;
  54. /** @var String */
  55. public $acctstartdelay;
  56. /** @var String */
  57. public $acctstopdelay;
  58. /** @var String */
  59. public $xascendsessionsvrkey;
  60. }
  61. /**
  62. * RadiusClient
  63. * RadiusClient
  64. */
  65. class RadiusClient {
  66. /** @var String */
  67. public $nasname;
  68. /** @var String */
  69. public $shortname;
  70. /** @var String */
  71. public $type;
  72. /** @var String */
  73. public $ports;
  74. /** @var String */
  75. public $secret;
  76. /** @var String */
  77. public $community;
  78. /** @var String */
  79. public $description;
  80. /** @var String */
  81. public $server;
  82. /** @var String */
  83. public $acct_enabled;
  84. }
  85. /**
  86. * RadiusRecord
  87. * RadiusRecord
  88. */
  89. class RadiusRecord{
  90. /** @var String */
  91. public $user;
  92. /** @var String */
  93. public $password;
  94. /** @var RadiusGroup */
  95. public $group;
  96. /** @var RadiusProp[] */
  97. public $reply;
  98. /** @var RadiusProp[] */
  99. public $check;
  100. }
  101. class RadiusProp{
  102. /** @var String */
  103. public $name;
  104. /** @var String */
  105. public $value;
  106. }
  107. class RadiusGroup{
  108. /** @var String */
  109. public $name;
  110. /** @var RadiusProp[] */
  111. public $reply;
  112. /** @var RadiusProp[] */
  113. public $check;
  114. public function getCheckArray(){
  115. $rtr = array();
  116. foreach($this->check as $r){
  117. $rtr[$r->name] = $r->value;
  118. }
  119. return $rtr;
  120. }
  121. public function getReplyArray(){
  122. $rtr = array();
  123. foreach($this->reply as $r){
  124. $rtr[$r->name] = $r->value;
  125. }
  126. return $rtr;
  127. }
  128. }
  129. class AccessServiceManager
  130. {
  131. private $sql_host = 'mysql';
  132. private $sql_user = "root";
  133. private $sql_pass = "";
  134. private $sql_db = 'freeradius';
  135. private $delete_group = true;
  136. private function getSqlCon(){
  137. $sql_db = $this->sql_db;
  138. $sql_host = $this->sql_host;
  139. $sql_user = "root"; //(getenv("MYSQL_USER"))?(getenv("MYSQL_USER")):$this->sql_user;
  140. $sql_pass = (getenv("MYSQL_ROOT_PASSWORD"))?(getenv("MYSQL_ROOT_PASSWORD")):$this->sql_pass;
  141. $dbh = new PDO("mysql:host=$sql_host;dbname=$sql_db", $sql_user, $sql_pass);
  142. return $dbh;
  143. }
  144. /**
  145. *Add a RadiusRecord on the Radius database in case the record exists the record is updated it returns True on success and False if a failure exists
  146. * @param RadiusRecord
  147. * @return Boolean
  148. */
  149. function addRadiusRecord(RadiusRecord $rr){
  150. $this->deleteRadiusRecord($rr);
  151. $this->addRadiusGroup($rr->group);
  152. syslog(LOG_DEBUG, __FUNCTION__);
  153. $dbh = $this->getSqlCon();
  154. $login = $rr->user;
  155. $Passwd = $rr->password;
  156. $values[] = "('$login','Password',':=','$Passwd')";
  157. foreach($rr->check as $k){
  158. $values[] = sprintf("('$login','%s',':=','%s')", $k->name, $k->value);
  159. }
  160. $values = implode(',', $values);
  161. $sql = "INSERT INTO radcheck (UserName,Attribute,op,Value) VALUES $values;";
  162. $rtr0 = $dbh->exec($sql);
  163. syslog(LOG_ALERT, $sql);
  164. $group = $rr->group;
  165. $sql="INSERT INTO radusergroup (UserName,GroupName) VALUES ('$login', '$group->name');";
  166. $rtr1 = $dbh->exec($sql);
  167. syslog(LOG_DEBUG, $sql);
  168. $rtr2 = true;
  169. $replys = array();
  170. foreach($rr->reply as $k)
  171. $replys[] = sprintf("('$login','%s','=','%s')", $k->name, $k->value);
  172. $rtr2 = true;
  173. if(!empty($replys)){
  174. $replysValue = implode(',', $replys);
  175. $sql="INSERT INTO radreply (UserName,Attribute,op,Value) VALUES $replysValue;";
  176. $rtr2 = $dbh->exec($sql);
  177. syslog(LOG_ALERT, $sql);
  178. }
  179. if($rtr0 and $rtr1 and $rtr2) return true;
  180. else return false;
  181. }
  182. protected function getRadiusGroupFromDb($groupName){
  183. $dbh = $this->getSqlCon();
  184. $tables = array('reply' => 'radgroupreply', 'check' => 'radgroupcheck');
  185. $rg_db = new RadiusGroup;
  186. $rg_db->name = $groupName;
  187. foreach($tables as $key => $table){
  188. $sql = sprintf("SELECT id, Attribute, op, Value
  189. FROM %s WHERE GroupName='%s'", $table, $groupName);
  190. $rtr = $dbh->query($sql);
  191. $props = array();
  192. foreach($rtr as $rr){
  193. $props[] = $p = new RadiusProp;
  194. $p->name = $rr['Attribute'];
  195. $p->value = $rr['Value'];
  196. }
  197. $rg_db->$key = $props;
  198. }
  199. return $rg_db;
  200. }
  201. protected function safeRadiusGroup(RadiusGroup $rg){
  202. $rg->name = preg_replace("|[^A-Za-z0-9]|", "-", $rg->name);
  203. return $rg;
  204. }
  205. /**
  206. * @param RadiusGroup
  207. *
  208. * @return Boolean
  209. */
  210. function deleteRadiusGroup(RadiusGroup $rg)
  211. {
  212. $rg = $this->safeRadiusGroup($rg);
  213. $sql = sprintf("SELECT COUNT( * ) AS `Filas` , `groupname` FROM `radusergroup` WHERE groupname = '%s' GROUP BY `groupname`", $rg->name);
  214. $dbh = $this->getSqlCon();
  215. $rtr = $dbh->query($sql);
  216. $total = 0;
  217. foreach($rtr as $t) $total = $t['Filas'];
  218. if($total == 0){
  219. $sql = "DELETE FROM `%s` WHERE GroupName = '%s'";
  220. foreach(array('radgroupcheck', 'radgroupreply',) as $table)
  221. $dbh->exec(sprintf($sql,$table, $rg->name));
  222. }
  223. //Residuos
  224. $sql = "DELETE FROM `radgroupcheck` WHERE `GroupName` NOT IN (SELECT DISTINCT (`groupname`) FROM `radusergroup` WHERE 1);";
  225. $dbh->exec($sql);
  226. $sql = "DELETE FROM `radgroupreply` WHERE `GroupName` NOT IN (SELECT DISTINCT (`groupname`) FROM `radusergroup` WHERE 1);";
  227. $dbh->exec($sql);
  228. return true;
  229. }
  230. /**
  231. * @param RadiusGroup
  232. *
  233. * @return Boolean
  234. */
  235. function addRadiusGroup(RadiusGroup $rg)
  236. {
  237. $rg = $this->safeRadiusGroup($rg);
  238. $dbh = $this->getSqlCon();
  239. $rgDB = $this->getRadiusGroupFromDb($rg->name);
  240. $props = array('check' => $rg->getCheckArray(), 'reply' => $rg->getReplyArray());
  241. $propsDB = array('check' => $rgDB->getCheckArray(), 'reply' => $rgDB->getReplyArray());
  242. foreach(array('check' => array('table' => 'radgroupcheck', 'op' => ':='),
  243. 'reply' => array('table' => 'radgroupreply', 'op' => '=')) as $op => $config) {
  244. $table = $config['table'];
  245. $key = array_keys($props[$op]);
  246. $keyDB = array_keys($propsDB[$op]);
  247. $agregar = array_diff($key, $keyDB);
  248. $borrar = array_diff($keyDB, $key);
  249. $actualizar = array_diff($key, array_merge($agregar, $borrar));
  250. if(!empty($borrar)){
  251. $borrar = array_map(array($dbh, "quote"), $borrar);
  252. $sql = sprintf("DELETE FROM %s WHERE GroupName='%s' AND Attribute IN (%s);",
  253. $table, $rg->name, implode(", ", $borrar));
  254. $dbh->exec($sql);
  255. }
  256. if(!empty($agregar)){
  257. foreach($agregar as $attribute){
  258. $sql = sprintf("INSERT INTO `%s` ( `GroupName` , `Attribute` , `op` , `Value` ) VALUES (%s, %s, %s, %s);", $table,
  259. $dbh->quote($rg->name),
  260. $dbh->quote($attribute),
  261. $dbh->quote($config['op']),
  262. $dbh->quote($props[$op][$attribute]));
  263. $dbh->exec($sql);
  264. }
  265. }
  266. if(!empty($actualizar)){
  267. foreach($actualizar as $attribute){
  268. $sql = sprintf("UPDATE `%s` SET `op`='%s', `Value` = %s WHERE GroupName=%s AND Attribute=%s;",
  269. $table,
  270. $config['op'],
  271. $dbh->quote($props[$op][$attribute]), //Value
  272. $dbh->quote($rg->name), //GroupName
  273. $dbh->quote($attribute) //GroupName
  274. );
  275. $dbh->exec($sql);
  276. }
  277. }
  278. }
  279. return true;
  280. }
  281. /**
  282. *Update a RadiusRecord on the Radius database in case the record dosn't exists the record is updated
  283. * it returns True on success and False if a failure exists
  284. * @param RadiusRecord
  285. * @return boolean
  286. */
  287. function updateRadiusRecord(RadiusRecord $rr){
  288. $this->deleteRadiusRecord($rr);
  289. return $this->addRadiusRecord($rr);
  290. }
  291. /**
  292. *Delete a RadiusRecord from the Radius database.
  293. * it returns True on success and False if a failure exists, such as the Record is not found.
  294. * @param RadiusRecord $rr
  295. * @return boolean
  296. */
  297. function deleteRadiusRecord(RadiusRecord $rr){
  298. $dbh = $this->getSqlCon();
  299. $login = $rr->user;
  300. $Passwd = $rr->password;
  301. $sql = "DELETE FROM radcheck WHERE UserName='$login';";
  302. $rtr0 = $dbh->exec($sql);
  303. if(empty($rr->group)) $rr->group = 'Full';
  304. $group = $rr->group;
  305. $sql="DELETE FROM radusergroup WHERE username='$login';";
  306. $rtr1 = $dbh->exec($sql);
  307. $rtr2 = true;
  308. $sql="DELETE FROM radreply WHERE UserName='$login';";
  309. $rtr2 = $dbh->exec($sql);
  310. $this->deleteRadiusGroup($rr->group);
  311. shell_exec($cmd = sprintf("php /etc/freeradius/close_radius_session.php --username=%s &", $login));
  312. syslog(LOG_INFO, $cmd);
  313. $this->disconnectRadiusRecord($rr);
  314. return $rtr0;
  315. }
  316. /**
  317. * Send a disconnect message to the nas server on all of the user active connections
  318. * @param RadiusRecord $rr
  319. * @return int number of connection succesfully reseted.
  320. */
  321. function disconnectRadiusRecord(RadiusRecord $rr){
  322. $mac_auth = false;
  323. if(preg_match("/([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i",$rr->user,$matches)){
  324. $mac_auth = true;
  325. }
  326. $dbh = $this->getSqlCon();
  327. $nasSQL = "SELECT nasname, secret FROM nas";
  328. $nasInfo = array(); $rtr = 0;
  329. foreach($dbh->query($nasSQL) as $nas){
  330. if(preg_match( "/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])" .
  331. "(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $nas['nasname']))
  332. $ip = $nas['nasname'];
  333. else $ip = gethostbyname($nas['nasname']);
  334. $nasInfo[$ip] = $nas['secret'];
  335. }
  336. $sql = "SELECT * FROM `radacct` WHERE `username` = ? AND ( `acctstoptime` IS NULL) ORDER BY `radacct`.`radacctid` DESC";
  337. $stmt = $dbh->prepare($sql);
  338. $rtr0 = $stmt->execute(array($rr->user));
  339. $return_var = 0;
  340. $rads = $stmt->fetchall();
  341. foreach ($rads as $rad) {
  342. $force_close = false;
  343. $user = $rr->user;
  344. if($mac_auth){
  345. unset($matches[0]);
  346. $mac_user1 = implode(":",$matches);
  347. $mac_user2 = implode("-",$matches);
  348. if($rad['callingstationid'] == $mac_user1 or $rad['callingstationid'] === $mac_user2 ){
  349. $user = $rad['callingstationid'];
  350. }
  351. }
  352. if(isset($nasInfo[$rad['nasipaddress']])){
  353. $cmd = sprintf("/bin/bash " . getcwd()."/scripts/raddisconnect.sh %s %s %s",
  354. escapeshellarg($rad['nasipaddress']),
  355. escapeshellarg($nasInfo[$rad['nasipaddress']]),
  356. escapeshellarg($user));
  357. if(!empty($rad['framedipaddress'])){
  358. $cmd = sprintf("%s %s", $cmd, escapeshellarg($rad['framedipaddress']));
  359. }
  360. $rtr_cmd= system ( "$cmd", $return_var);
  361. $radacctid = $rad['radacctid'];
  362. if($return_var == 0){
  363. $rtr++;
  364. syslog(LOG_INFO, basename(__FILE__) . " running cmd (OK) (radacctid $radacctid) ROW {$stmt->rowCount()}" . $cmd);
  365. }else{
  366. syslog(LOG_INFO, basename(__FILE__) . " running cmd (FAIL - $return_var) (radacctid $radacctid) ROW {$stmt->rowCount()}" . $cmd);
  367. $force_close = true;
  368. }
  369. }else{
  370. syslog(LOG_ALERT, sprintf("Can't find the nas %s IP on the nas table.",$rad['nasipaddress']));
  371. $force_close = true;
  372. }
  373. if($force_close){
  374. $sql = sprintf("UPDATE `radius`.`radacct` SET `acctstoptime` = NOW( ) ,
  375. `acctterminatecause` = 'unresponsive session (radius WS)' WHERE
  376. `radacct`.`radacctid` =%d", $rad['radacctid']);
  377. $dbh->exec($sql);
  378. }
  379. }
  380. return $rtr;
  381. }
  382. /**
  383. *Search all RadiusRecord with the matching pattern
  384. * @param String $pattern the patter to search for
  385. * @param Boolean $exact specify if the match shuld be exact otherwise, will match ani username containing the patter
  386. * @return RadiusRecord[]
  387. */
  388. function listRadiusRecord($pattern, $exact = true){
  389. if(is_null($exact)) $exact = true;
  390. $dbh = $this->getSqlCon();
  391. $sql = "SELECT UserName, Value FROM radcheck WHERE Attribute='Password' AND op=':=' AND UserName LIKE '%s'";
  392. if(!$exact) $pattern = '%' . $pattern . '%';
  393. $sql = sprintf($sql, $pattern);
  394. $rtr = array();
  395. foreach($dbh->query($sql) as $rr){
  396. $rtr[$rr['UserName']] = $r = new RadiusRecord();
  397. $r->user = $rr['UserName'];
  398. $r->password = $rr['Value'];
  399. }
  400. $sql = "SELECT UserName, GroupName FROM radusergroup WHERE UserName LIKE '%s'";
  401. $sql = sprintf($sql, $pattern);
  402. foreach($dbh->query($sql) as $info){
  403. if(($rtr[$info['UserName']]))
  404. {
  405. $rr = $rtr[$info['UserName']];
  406. $rr->group = $info['GroupName'];
  407. }
  408. }
  409. $sql = "SELECT UserName, Value FROM radreply WHERE Attribute='Framed-IP-Address' AND op = '=' AND UserName LIKE '%s'";
  410. $sql = sprintf($sql, $pattern);
  411. foreach($dbh->query($sql) as $info){
  412. if(($rtr[$info['UserName']]))
  413. {
  414. $rr = $rtr[$info['UserName']];
  415. $rr->ipAddress = $info['Value'];
  416. }
  417. }
  418. return $rtr;
  419. }
  420. /**
  421. * Add (or update) a entry in the NAS (radius Client) table
  422. * @param RadiusClient $radclient the radcliente, it is identified by its nasname (IP)
  423. *
  424. * @return Boolean
  425. */
  426. function addRadiusClient(RadiusClient $radcliente)
  427. {
  428. $dbh = $this->getSqlCon();
  429. $acctEnabled = $radcliente->acct_enabled ? 1 : 0;
  430. $stm = $dbh->prepare("SELECT * FROM nas WHERE nasname=:nasname");
  431. $stm->bindParam('nasname', $radcliente->nasname);
  432. $stm->execute();
  433. $existsNas = $stm->fetch(PDO::FETCH_ASSOC);
  434. if(!$existsNas){
  435. $stm1 = $dbh->prepare("INSERT INTO nas (nasname, shortname, type, ports, secret, community, description, server, acct_enabled)
  436. VALUES (:nasname, :shortname, :type, :ports, :secret, :community, :description, :server, :acct_enabled);");
  437. }else{
  438. $stm1 = $dbh->prepare("UPDATE nas SET
  439. nasname=:nasname,
  440. shortname=:shortname,
  441. type=:type,
  442. ports=:ports,
  443. secret=:secret,
  444. community=:community,
  445. description=:description,
  446. server=:server,
  447. acct_enabled=:acct_enabled WHERE nasname=:nasname
  448. ");
  449. }
  450. $stm1->bindParam('nasname', $radcliente->nasname);
  451. $stm1->bindParam('shortname', $radcliente->shortname);
  452. $stm1->bindParam('type', $radcliente->type);
  453. $stm1->bindParam('ports', $radcliente->ports);
  454. $stm1->bindParam('secret', $radcliente->secret);
  455. $stm1->bindParam('community', $radcliente->community);
  456. $stm1->bindParam('description', $radcliente->description);
  457. $stm1->bindParam('server', $radcliente->server);
  458. $stm1->bindParam('acct_enabled', $acctEnabled);
  459. $rtr1 = $stm1->execute();
  460. syslog(shell_exec('sudo kill -9 $(pgrep freeradius)'));
  461. return $rtr1;
  462. }
  463. /**
  464. * delete a entry in the NAS (radius Client) table
  465. * @param RadiusClient $radclient the radcliente, it is identified by its nasname (IP)
  466. *
  467. * @return Boolean
  468. */
  469. function deleteRadiusClient(RadiusClient $radcliente)
  470. {
  471. $dbh = $this->getSqlCon();
  472. $stm = $dbh->prepare("DELETE FROM nas WHERE nasname = ?;");
  473. $rtr = $stm->execute(array($radcliente->nasname));
  474. syslog(shell_exec('sudo kill -9 $(pgrep freeradius)'));
  475. return $rtr;
  476. }
  477. /**
  478. * Find all RadAct
  479. * @param RadAct $crit, NULL attributes are ignored in the query
  480. * @param Integer $inicio
  481. * @param Integer $count
  482. * @return RadAct[]
  483. */
  484. function findRadAct(RadAct $crit, $inicio, $count){
  485. $sql = "SELECT * FROM radacct";
  486. $where = array();
  487. $rtr = array();
  488. foreach($crit as $key => $val){
  489. if(!is_null($val))
  490. $where[] = "$key = '$val'";
  491. }
  492. if(!empty($where)){
  493. $sql .= " WHERE " . implode(' AND ', $where);
  494. }
  495. $sql .= " ORDER BY `acctstarttime` DESC";
  496. $sql .= " LIMIT $inicio, $count";
  497. $dbh = $this->getSqlCon();
  498. foreach($dbh->query($sql) as $row){
  499. $rtr [] = $obj = new RadAct;
  500. foreach($row as $k => $v) $obj->$k = $v;
  501. }
  502. return $rtr;
  503. }
  504. /**
  505. * Find RadAct for the $username address
  506. * @param String $username
  507. * @return RadAct[]
  508. */
  509. function getRadActByUsername($username)
  510. {
  511. $radAct = new RadAct();
  512. $radAct->username = $username;
  513. return $this->findRadAct($radAct, 0, 50);
  514. }
  515. }
  516. require_once "wshelper/common.php";
  517. $WSClasses = array('AccessServiceManager');
  518. $WSStructures = array(
  519. 'RadAct' => 'RadAct',
  520. 'RadiusGroup' => 'RadiusGroup',
  521. 'RadiusProp' => 'RadiusProp',
  522. 'RadiusRecord' => 'RadiusRecord',
  523. 'RadiusClient' => 'RadiusClient');
  524. require_once "servicioSoap.php";