From b71082ccdb3d821210b1b572ebde1839e3d23dac Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:09:17 +0100 Subject: [PATCH 01/15] Added a class to perform google autenticator checks --- pandora_console/include/auth/GAuth/Auth.php | 356 ++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 pandora_console/include/auth/GAuth/Auth.php diff --git a/pandora_console/include/auth/GAuth/Auth.php b/pandora_console/include/auth/GAuth/Auth.php new file mode 100644 index 0000000000..2aa859238d --- /dev/null +++ b/pandora_console/include/auth/GAuth/Auth.php @@ -0,0 +1,356 @@ + + * @package GAuth + * @license MIT + */ + +class Auth +{ + /** + * Internal lookup table + * @var array + */ + private $lookup = array(); + + /** + * Initialization key + * @var string + */ + private $initKey = null; + + /** + * Seconds between key refreshes + * @var integer + */ + private $refreshSeconds = 30; + + /** + * Length of codes to generate + * @var integer + */ + private $codeLength = 6; + + /** + * Range plus/minus for "window of opportunity" on allowed codes + * @var integer + */ + private $range = 2; + + /** + * Initialize the object and set up the lookup table + * Optionally the Initialization key + * + * @param string $initKey Initialization key + */ + public function __construct($initKey = null) + { + $this->buildLookup(); + + if ($initKey !== null) { + $this->setInitKey($initKey); + } + } + + /** + * Build the base32 lookup table + * + * @return null + */ + public function buildLookup() + { + $lookup = array_combine( + array_merge(range('A', 'Z'), range(2, 7)), + range(0, 31) + ); + $this->setLookup($lookup); + } + + /** + * Get the current "range" value + * @return integer Range value + */ + public function getRange() + { + return $this->range; + } + + /** + * Set the "range" value + * + * @param integer $range Range value + * @return \GAuth\Auth instance + */ + public function setRange($range) + { + if (!is_numeric($range)) { + throw new \InvalidArgumentException('Invalid window range'); + } + $this->range = $range; + return $this; + } + + /** + * Set the initialization key for the object + * + * @param string $key Initialization key + * @throws \InvalidArgumentException If hash is not valid base32 + * @return \GAuth\Auth instance + */ + public function setInitKey($key) + { + if (preg_match('/^['.implode('', array_keys($this->getLookup())).']+$/', $key) == false) { + throw new \InvalidArgumentException('Invalid base32 hash!'); + } + $this->initKey = $key; + return $this; + } + + /** + * Get the current Initialization key + * + * @return string Initialization key + */ + public function getInitKey() + { + return $this->initKey; + } + + /** + * Set the contents of the internal lookup table + * + * @param array $lookup Lookup data set + * @throws \InvalidArgumentException If lookup given is not an array + * @return \GAuth\Auth instance + */ + public function setLookup($lookup) + { + if (!is_array($lookup)) { + throw new \InvalidArgumentException('Lookup value must be an array'); + } + $this->lookup = $lookup; + return $this; + } + + /** + * Get the current lookup data set + * + * @return array Lookup data + */ + public function getLookup() + { + return $this->lookup; + } + + /** + * Get the number of seconds for code refresh currently set + * + * @return integer Refresh in seconds + */ + public function getRefresh() + { + return $this->refreshSeconds; + } + + /** + * Set the number of seconds to refresh codes + * + * @param integer $seconds Seconds to refresh + * @throws \InvalidArgumentException If seconds value is not numeric + * @return \GAuth\Auth instance + */ + public function setRefresh($seconds) + { + if (!is_numeric($seconds)) { + throw \InvalidArgumentException('Seconds must be numeric'); + } + $this->refreshSeconds = $seconds; + return $this; + } + + /** + * Get the current length for generated codes + * + * @return integer Code length + */ + public function getCodeLength() + { + return $this->codeLength; + } + + /** + * Set the length of the generated codes + * + * @param integer $length Code length + * @return \GAuth\Auth instance + */ + public function setCodeLength($length) + { + $this->codeLength = $length; + return $this; + } + + /** + * Validate the given code + * + * @param string $code Code entered by user + * @param string $initKey Initialization key + * @param string $timestamp Timestamp for calculation + * @param integer $range Seconds before/after to validate hash against + * @throws \InvalidArgumentException If incorrect code length + * @return boolean Pass/fail of validation + */ + public function validateCode($code, $initKey = null, $timestamp = null, $range = null) + { + if (strlen($code) !== $this->getCodeLength()) { + throw new \InvalidArgumentException('Incorrect code length'); + } + + $range = ($range == null) ? $this->getRange() : $range; + $timestamp = ($timestamp == null) ? $this->generateTimestamp() : $timestamp; + $initKey = ($initKey == null) ? $this->getInitKey() : $initKey; + + $binary = $this->base32_decode($initKey); + + for ($time = ($timestamp - $range); $time <= ($timestamp + $range); $time++) { + if ($this->generateOneTime($binary, $time) == $code) { + return true; + } + } + return false; + } + + /** + * Generate a one-time code + * + * @param string $initKey Initialization key [optional] + * @param string $timestamp Timestamp for calculation [optional] + * @return string Geneerated code/hash + */ + public function generateOneTime($initKey = null, $timestamp = null) + { + $initKey = ($initKey == null) ? $this->getInitKey() : $initKey; + $timestamp = ($timestamp == null) ? $this->generateTimestamp() : $timestamp; + + $hash = hash_hmac ( + 'sha1', + pack('N*', 0) . pack('N*', $timestamp), + $initKey, + true + ); + + return str_pad($this->truncateHash($hash), $this->getCodeLength(), '0', STR_PAD_LEFT); + } + + /** + * Generate a code/hash + * Useful for making Initialization codes + * + * @param integer $length Length for the generated code + * @return string Generated code + */ + public function generateCode($length = 16) + { + $lookup = implode('', array_keys($this->getLookup())); + $code = ''; + + for ($i = 0; $i < $length; $i++) { + $code .= $lookup[mt_rand(0, strlen($lookup)-1)]; + } + + return $code; + } + + /** + * Geenrate the timestamp for the calculation + * + * @return integer Timestamp + */ + public function generateTimestamp() + { + return floor(microtime(true)/$this->getRefresh()); + } + + /** + * Truncate the given hash down to just what we need + * + * @param string $hash Hash to truncate + * @return string Truncated hash value + */ + public function truncateHash($hash) + { + $offset = ord($hash[19]) & 0xf; + + return ( + ((ord($hash[$offset+0]) & 0x7f) << 24 ) | + ((ord($hash[$offset+1]) & 0xff) << 16 ) | + ((ord($hash[$offset+2]) & 0xff) << 8 ) | + (ord($hash[$offset+3]) & 0xff) + ) % pow(10, $this->getCodeLength()); + } + + /** + * Base32 decoding function + * + * @param string base32 encoded hash + * @throws \InvalidArgumentException When hash is not valid + * @return string Binary value of hash + */ + public function base32_decode($hash) + { + $lookup = $this->getLookup(); + + if (preg_match('/^['.implode('', array_keys($lookup)).']+$/', $hash) == false) { + throw new \InvalidArgumentException('Invalid base32 hash!'); + } + + $hash = strtoupper($hash); + $buffer = 0; + $length = 0; + $binary = ''; + + for ($i = 0; $i < strlen($hash); $i++) { + $buffer = $buffer << 5; + $buffer += $lookup[$hash[$i]]; + $length += 5; + + if ($length >= 8) { + $length -= 8; + $binary .= chr(($buffer & (0xFF << $length)) >> $length); + } + } + + return $binary; + } +} \ No newline at end of file From 3c1dbbdc73e306ab9c9b15aa597a8be2f52bb98f Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:10:15 +0100 Subject: [PATCH 02/15] File with double auth utilities to be accessed by ajax --- .../include/ajax/double_auth.ajax.php | 522 ++++++++++++++++++ 1 file changed, 522 insertions(+) create mode 100644 pandora_console/include/ajax/double_auth.ajax.php diff --git a/pandora_console/include/ajax/double_auth.ajax.php b/pandora_console/include/ajax/double_auth.ajax.php new file mode 100644 index 0000000000..770749e683 --- /dev/null +++ b/pandora_console/include/ajax/double_auth.ajax.php @@ -0,0 +1,522 @@ +generateCode($secret_lenght); + + echo json_encode($code); + return; +} + +// Validate the provided secret with a code provided by the user. +// If the parameter 'save' is set to true, the secret will +// be stored into the database. +// The results can be true, false or 1 if the validation is true +// but the secret can't be stored into the database. +$validate_double_auth_code = (bool) get_parameter('validate_double_auth_code'); +if ($validate_double_auth_code) { + $result = false; + + $secret = (string) get_parameter('secret'); + + if (!empty($secret) && strlen($secret) === $secret_lenght) { + $code = (string) get_parameter('code'); + + if (!empty($code) && strlen($code) === $code_lenght) { + $save = (bool) get_parameter('save'); + + if (!empty($code)) { + $gAuth = new \GAuth\Auth($secret); + $result = $gAuth->validateCode($code); + } + + if ($result && $save) { + // Delete the actual value (if exists) + $where = array( + 'id_user' => $id_user + ); + db_process_sql_delete('tuser_double_auth', $where); + + // Insert the new value + $values = array( + 'id_user' => $id_user, + 'secret' => $secret + ); + $result = (bool) db_process_sql_insert('tuser_double_auth', $values); + + if (!$result) { + $result = 1; + } + } + } + } + + echo json_encode($result); + return; +} + +// Set the provided secret to the user +$save_double_auth_secret = (bool) get_parameter('save_double_auth_secret'); +if ($save_double_auth_secret) { + $result = false; + + $secret = (string) get_parameter('secret'); + + if (strlen($secret) === $secret_lenght) { + // Delete the actual value (if exists) + $where = array( + 'id_user' => $id_user + ); + db_process_sql_delete('tuser_double_auth', $where); + // Insert the new value + $values = array( + 'id_user' => $id_user, + 'secret' => $secret + ); + $result = (bool) db_process_sql_insert('tuser_double_auth', $values); + } + + echo json_encode($result); + return; +} + +// Disable the double auth for the user +$deactivate_double_auth = (bool) get_parameter('deactivate_double_auth'); +if ($deactivate_double_auth) { + $result = false; + + // Delete the actual value (if exists) + $where = array( + 'id_user' => $id_user + ); + $result = db_process_sql_delete('tuser_double_auth', $where); + + echo json_encode($result); + return; +} + +// Get the info page to the container dialog +$get_double_auth_data_page = (bool) get_parameter('get_double_auth_data_page'); +if ($get_double_auth_data_page) { + $secret = db_get_value('secret', 'tuser_double_auth', 'id_user', $id_user); + + if (empty($secret)) { + return; + } + + $html = ''; + $html .= "
"; + $html .= "

"; + $html .= __('This is the private code that you should use with your authenticator app') . ". "; + $html .= __('You could enter the code manually or use the QR code to add it automatically') . "."; + $html .= "

"; + $html .= "
"; + $html .= "
"; + $html .= __('Code') . ": $secret"; + $html .= "
"; + $html .= __('QR') . ":
"; + $html .= "
"; + $html .= "
"; + + ob_clean(); +?> + +"; + $html .= "

"; + $html .= __('You are about to activate the double authentication') . ". "; + $html .= __('With this option enabled, your account access will be more secure, + cause a code generated by other application will be required after the login') . ". "; + $html .= "

"; + $html .= "

"; + $html .= __('You will need to install the app from the following link before continue') . ". "; + $html .= "

"; + $html .= ""; + $html .= "
"; + $html .= "
"; + $html .= html_print_button(__('Download the app'), 'google_authenticator_download', false, '', '', true); + $html .= "
"; + $html .= "
"; + $html .= "
"; + $html .= html_print_button(__('Continue'), 'continue_to_generate', false, '', '', true); + $html .= "
"; + + ob_clean(); +?> + +generateCode($secret_lenght); + + $html = ''; + $html .= "
"; + $html .= "

"; + $html .= "" . __('A private code has been generated') . "."; + $html .= "

"; + $html .= "
"; + $html .= "
"; + $html .= "

"; + $html .= __('Before continue, you should create a new entry into the authenticator app') . ". "; + $html .= __('You could enter the code manually or use the QR code to add it automatically') . "."; + $html .= "

"; + $html .= "
"; + $html .= "
"; + $html .= __('Code') . ": $secret"; + $html .= "
"; + $html .= __('QR') . ":
"; + $html .= "
"; + $html .= "
"; + $html .= html_print_button(__('Refresh code'), 'continue_to_generate', false, '', '', true); + $html .= " "; + $html .= html_print_button(__('Continue'), 'continue_to_validate', false, '', '', true); + $html .= "
"; + + ob_clean(); +?> + +"; + $html .= "

"; + $html .= __('Introduce a code generated by the app') . ". "; + $html .= __('If the code is valid, the double authentication will be activated') . "."; + $html .= "

"; + $html .= ""; + $html .= "
"; + $html .= "
"; + $html .= html_print_input_text('code', '', '', 50, $secret_lenght, true); + $html .= "
"; + $html .= "

"; + $html .= "
"; + $html .= html_print_button(__('Validate code'), 'continue_to_validate', false, '', '', true); + $html .= html_print_image ("images/spinner.gif", true); + $html .= "
"; + $html .= "
"; + + ob_clean(); +?> + + \ No newline at end of file From d399ccbd1feea7bcbdc0b330267b78a6469b7d21 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:11:21 +0100 Subject: [PATCH 03/15] Added a new table called 'tuser_double_auth' --- .../extras/pandoradb_migrate_5.1_to_6.0.mysql.sql | 12 ++++++++++++ .../extras/pandoradb_migrate_5.1_to_6.0.oracle.sql | 14 +++++++++++++- .../pandoradb_migrate_5.1_to_6.0.postgreSQL.sql | 12 +++++++++++- pandora_console/pandoradb.oracle.sql | 11 +++++++++++ pandora_console/pandoradb.postgreSQL.sql | 9 +++++++++ pandora_console/pandoradb.sql | 12 ++++++++++++ 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.mysql.sql b/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.mysql.sql index b5a8ca7aa3..1f6e37bcac 100755 --- a/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.mysql.sql +++ b/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.mysql.sql @@ -14,3 +14,15 @@ ALTER TABLE tlayout_data ADD COLUMN `border_width` INTEGER UNSIGNED NOT NULL def ALTER TABLE tlayout_data ADD COLUMN `border_color` varchar(200) DEFAULT ""; ALTER TABLE tlayout_data ADD COLUMN `fill_color` varchar(200) DEFAULT ""; +/* 2014/12/10 */ +-- ---------------------------------------------------------------------- +-- Table `tuser_double_auth` +-- ---------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tuser_double_auth` ( + `id` int(10) unsigned NOT NULL auto_increment, + `id_user` varchar(60) NOT NULL, + `secret` varchar(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE (`id_user`), + FOREIGN KEY (`id_user`) REFERENCES tusuario(`id_user`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.oracle.sql b/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.oracle.sql index 182394c445..b3cdb6b13c 100755 --- a/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.oracle.sql +++ b/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.oracle.sql @@ -12,4 +12,16 @@ ALTER TABLE tlayout_data DROP COLUMN no_link_color; ALTER TABLE tlayout_data DROP COLUMN label_color; ALTER TABLE tlayout_data ADD COLUMN border_width INTEGER NOT NULL default 0; ALTER TABLE tlayout_data ADD COLUMN border_color varchar(200) DEFAULT ""; -ALTER TABLE tlayout_data ADD COLUMN fill_color varchar(200) DEFAULT ""; \ No newline at end of file +ALTER TABLE tlayout_data ADD COLUMN fill_color varchar(200) DEFAULT ""; + +/* 2014/12/10 */ +-- ---------------------------------------------------------------------- +-- Table `tuser_double_auth` +-- ---------------------------------------------------------------------- +CREATE TABLE tuser_double_auth ( + id NUMBER(10, 0) NOT NULL PRIMARY KEY, + id_user VARCHAR2(60) NOT NULL REFERENCES tusuario(id_user) ON DELETE CASCADE, + secret VARCHAR2(20) NOT NULL +); +CREATE SEQUENCE tuser_double_auth_s INCREMENT BY 1 START WITH 1; +CREATE OR REPLACE TRIGGER tuser_double_auth_inc BEFORE INSERT ON tuser_double_auth REFERENCING NEW AS NEW FOR EACH ROW BEGIN SELECT tuser_double_auth_s.nextval INTO :NEW.ID FROM dual; END tuser_double_auth_inc;; \ No newline at end of file diff --git a/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.postgreSQL.sql b/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.postgreSQL.sql index 81ae7e6c05..8fa2e6346c 100755 --- a/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.postgreSQL.sql +++ b/pandora_console/extras/pandoradb_migrate_5.1_to_6.0.postgreSQL.sql @@ -12,4 +12,14 @@ ALTER TABLE "tlayout_data" DROP COLUMN "no_link_color"; ALTER TABLE "tlayout_data" DROP COLUMN "label_color"; ALTER TABLE "tlayout_data" ADD COLUMN "border_width" INTEGER NOT NULL default 0; ALTER TABLE "tlayout_data" ADD COLUMN "border_color" varchar(200) DEFAULT ""; -ALTER TABLE "tlayout_data" ADD COLUMN "fill_color" varchar(200) DEFAULT ""; \ No newline at end of file +ALTER TABLE "tlayout_data" ADD COLUMN "fill_color" varchar(200) DEFAULT ""; + +/* 2014/12/10 */ +-- ---------------------------------------------------------------------- +-- Table `tuser_double_auth` +-- ---------------------------------------------------------------------- +CREATE TABLE "tuser_double_auth" ( + "id" SERIAL NOT NULL PRIMARY KEY, + "id_user" varchar(60) NOT NULL UNIQUE REFERENCES "tusuario"("id_user") ON DELETE CASCADE, + "secret" varchar(20) NOT NULL +); \ No newline at end of file diff --git a/pandora_console/pandoradb.oracle.sql b/pandora_console/pandoradb.oracle.sql index a97ba1e163..a7af86524b 100755 --- a/pandora_console/pandoradb.oracle.sql +++ b/pandora_console/pandoradb.oracle.sql @@ -1047,6 +1047,17 @@ CREATE TABLE tusuario_perfil ( CREATE SEQUENCE tusuario_perfil_s INCREMENT BY 1 START WITH 1; CREATE OR REPLACE TRIGGER tusuario_perfil_inc BEFORE INSERT ON tusuario_perfil REFERENCING NEW AS NEW FOR EACH ROW BEGIN SELECT tusuario_perfil_s.nextval INTO :NEW.ID_UP FROM dual; END tusuario_perfil_inc;; +-- ---------------------------------------------------------------------- +-- Table `tuser_double_auth` +-- ---------------------------------------------------------------------- +CREATE TABLE tuser_double_auth ( + id NUMBER(10, 0) NOT NULL PRIMARY KEY, + id_user VARCHAR2(60) NOT NULL REFERENCES tusuario(id_user) ON DELETE CASCADE, + secret VARCHAR2(20) NOT NULL +); +CREATE SEQUENCE tuser_double_auth_s INCREMENT BY 1 START WITH 1; +CREATE OR REPLACE TRIGGER tuser_double_auth_inc BEFORE INSERT ON tuser_double_auth REFERENCING NEW AS NEW FOR EACH ROW BEGIN SELECT tuser_double_auth_s.nextval INTO :NEW.ID FROM dual; END tuser_double_auth_inc;; + -- --------------------------------------------------------------------- -- Table "tnews" -- --------------------------------------------------------------------- diff --git a/pandora_console/pandoradb.postgreSQL.sql b/pandora_console/pandoradb.postgreSQL.sql index 20fd07b0ae..1ae3d6f4c5 100755 --- a/pandora_console/pandoradb.postgreSQL.sql +++ b/pandora_console/pandoradb.postgreSQL.sql @@ -926,6 +926,15 @@ CREATE TABLE "tusuario_perfil" ( "tags" text NOT NULL ); +-- ---------------------------------------------------------------------- +-- Table `tuser_double_auth` +-- ---------------------------------------------------------------------- +CREATE TABLE "tuser_double_auth" ( + "id" SERIAL NOT NULL PRIMARY KEY, + "id_user" varchar(60) NOT NULL UNIQUE REFERENCES "tusuario"("id_user") ON DELETE CASCADE, + "secret" varchar(20) NOT NULL +); + -- ----------------------------------------------------- -- Table `tnews` -- ----------------------------------------------------- diff --git a/pandora_console/pandoradb.sql b/pandora_console/pandoradb.sql index 376ae670aa..6abc69e96c 100755 --- a/pandora_console/pandoradb.sql +++ b/pandora_console/pandoradb.sql @@ -999,6 +999,18 @@ CREATE TABLE IF NOT EXISTS `tusuario_perfil` ( PRIMARY KEY (`id_up`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- ---------------------------------------------------------------------- +-- Table `tuser_double_auth` +-- ---------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `tuser_double_auth` ( + `id` int(10) unsigned NOT NULL auto_increment, + `id_user` varchar(60) NOT NULL, + `secret` varchar(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE (`id_user`), + FOREIGN KEY (`id_user`) REFERENCES tusuario(`id_user`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + -- ---------------------------------------------------------------------- -- Table `tnews` -- ---------------------------------------------------------------------- From 33c678914410f1a6a3d24b219a8174306ef6a8c4 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:12:27 +0100 Subject: [PATCH 04/15] Added an option to allow the users to enable the 2 step auth --- pandora_console/godmode/setup/setup_auth.php | 9 +++++++++ pandora_console/include/functions_config.php | 2 ++ 2 files changed, 11 insertions(+) diff --git a/pandora_console/godmode/setup/setup_auth.php b/pandora_console/godmode/setup/setup_auth.php index 2c29c91633..1a077e1542 100644 --- a/pandora_console/godmode/setup/setup_auth.php +++ b/pandora_console/godmode/setup/setup_auth.php @@ -94,6 +94,15 @@ if (enterprise_installed()) { add_enterprise_auth_options($table, 12); } +// Enable double authentication +$row = array(); +$row[] = __('Double authentication') + . ui_print_help_tip(__("If this option is enabled, the users can use double authentication with their accounts"), true); +$row[] = __('Yes').' '.html_print_radio_button('double_auth_enabled', 1, '', $config['double_auth_enabled'], true) + .'  ' + . __('No').' '.html_print_radio_button('double_auth_enabled', 0, '', $config['double_auth_enabled'], true); +$table->data[] = $row; + echo '
'; html_print_input_hidden ('update_config', 1); html_print_table ($table); diff --git a/pandora_console/include/functions_config.php b/pandora_console/include/functions_config.php index 4d87e9b815..daf12e9367 100644 --- a/pandora_console/include/functions_config.php +++ b/pandora_console/include/functions_config.php @@ -321,6 +321,8 @@ function config_update_config () { $error_update[] = __('User'); if (!config_update_value ('rintegria_pass', get_parameter ('rintegria_pass'))) $error_update[] = __('Password'); + if (!config_update_value ('double_auth_enabled', get_parameter ('double_auth_enabled'))) + $error_update[] = __('Double authentication'); ///////////// break; case 'perf': From 7caf016e4d2b834e693c3d5933afa53530b04479 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:16:36 +0100 Subject: [PATCH 05/15] Added some new rules --- pandora_console/include/styles/pandora.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pandora_console/include/styles/pandora.css b/pandora_console/include/styles/pandora.css index 599c6c32f9..c910ec159b 100755 --- a/pandora_console/include/styles/pandora.css +++ b/pandora_console/include/styles/pandora.css @@ -2807,4 +2807,18 @@ table#policy_modules td * { margin-bottom: 10px; border-top-left-radius: 2px; border-top-right-radius: 2px; +} + +#dialog-double_auth-container { + width: 100%; + text-align: center; + vertical-align: middle; +} + +.center_align { + text-align: center; +} + +.left_align { + text-align: left; } \ No newline at end of file From 96511fe25a2ea2d11d9c34d4a5345eda06d09d3a Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:17:35 +0100 Subject: [PATCH 06/15] Now the user can enable and configure the 2 step auth --- pandora_console/operation/users/user_edit.php | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/pandora_console/operation/users/user_edit.php b/pandora_console/operation/users/user_edit.php index ca2858d378..3068cd3f9f 100644 --- a/pandora_console/operation/users/user_edit.php +++ b/pandora_console/operation/users/user_edit.php @@ -337,6 +337,23 @@ if (!$meta) { $table->data[] = $data; } +// Double auth +$double_auth_enabled = (bool) db_get_value('id', 'tuser_double_auth', 'id_user', $config['id_user']); +$data = array(); +$data[0] = __('Double authentication'); +$data[0] .= '
'; +$data[0] .= html_print_checkbox('double_auth', 1, $double_auth_enabled, true); +if ($double_auth_enabled) { + $data[0] .= '  '; + $data[0] .= html_print_button(__('Show information'), 'show_info', false, 'javascript:show_double_auth_info();', '', true); +} +// Dialog +$data[0] .= "
"; +$table->colspan[count($table->data)][0] = 3; +$table->rowclass[] = ''; +$table->rowstyle[] = 'font-weight: bold;'; +$table->data[] = $data; + $data = array(); $data[0] = __('Comments'); $table->colspan[count($table->data)][0] = 3; @@ -431,6 +448,17 @@ $(document).ready (function () { $("#text-block_size").removeAttr('disabled'); } } + + $("input#checkbox-double_auth").change(function (e) { + e.preventDefault(); + + if (this.checked) { + show_double_auth_activation(); + } + else { + show_double_auth_deactivation(); + } + }); show_data_section(); }); @@ -481,4 +509,209 @@ function show_data_section () { break; } } + +function show_double_auth_info () { + var userID = ""; + + var $loadingSpinner = $("/images/spinner.gif\" />"); + var $dialogContainer = $("div#dialog-double_auth-container"); + + $dialogContainer.html($loadingSpinner); + + // Load the info page + var request = $.ajax({ + url: 'ajax.php', + type: 'POST', + dataType: 'html', + data: { + page: 'include/ajax/double_auth.ajax', + id_user: userID, + get_double_auth_data_page: 1, + containerID: $dialogContainer.prop('id') + }, + complete: function(xhr, textStatus) { + + }, + success: function(data, textStatus, xhr) { + // isNaN = is not a number + if (isNaN(data)) { + $dialogContainer.html(data); + } + // data is a number, convert it to integer to do the compare + else if (Number(data) === -1) { + $dialogContainer.html(""); + } + else { + $dialogContainer.html(""); + } + }, + error: function(xhr, textStatus, errorThrown) { + $dialogContainer.html(""); + } + }); + + $("div#dialog-double_auth") + .append($dialogContainer) + .dialog({ + resizable: true, + draggable: true, + modal: true, + title: "", + overlay: { + opacity: 0.5, + background: "black" + }, + width: 400, + height: 375, + close: function(event, ui) { + // Abort the ajax request + if (typeof request != 'undefined') + request.abort(); + // Remove the contained html + $dialogContainer.empty(); + } + }) + .show(); + +} + +function show_double_auth_activation () { + var userID = ""; + + var $loadingSpinner = $("/images/spinner.gif\" />"); + var $dialogContainer = $("div#dialog-double_auth-container"); + + $dialogContainer.html($loadingSpinner); + + // Load the info page + var request = $.ajax({ + url: 'ajax.php', + type: 'POST', + dataType: 'html', + data: { + page: 'include/ajax/double_auth.ajax', + id_user: userID, + get_double_auth_info_page: 1, + containerID: $dialogContainer.prop('id') + }, + complete: function(xhr, textStatus) { + + }, + success: function(data, textStatus, xhr) { + // isNaN = is not a number + if (isNaN(data)) { + $dialogContainer.html(data); + } + // data is a number, convert it to integer to do the compare + else if (Number(data) === -1) { + $dialogContainer.html(""); + } + else { + $dialogContainer.html(""); + } + }, + error: function(xhr, textStatus, errorThrown) { + $dialogContainer.html(""); + } + }); + + $("div#dialog-double_auth").dialog({ + resizable: true, + draggable: true, + modal: true, + title: "", + overlay: { + opacity: 0.5, + background: "black" + }, + width: 500, + height: 400, + close: function(event, ui) { + // Abort the ajax request + if (typeof request != 'undefined') + request.abort(); + // Remove the contained html + $dialogContainer.empty(); + + document.location.reload(); + } + }) + .show(); +} + +function show_double_auth_deactivation () { + var userID = ""; + + var $loadingSpinner = $("/images/spinner.gif\" />"); + var $dialogContainer = $("div#dialog-double_auth-container"); + + var message = "

' . __('The double authentication will be deactivated'); ?>

"; + var $button = $("\" />"); + + $dialogContainer + .empty() + .append(message) + .append($button); + + var request; + + $button.click(function(e) { + e.preventDefault(); + + $dialogContainer.html($loadingSpinner); + + // Deactivate the double auth + request = $.ajax({ + url: 'ajax.php', + type: 'POST', + dataType: 'json', + data: { + page: 'include/ajax/double_auth.ajax', + id_user: userID, + deactivate_double_auth: 1 + }, + complete: function(xhr, textStatus) { + + }, + success: function(data, textStatus, xhr) { + if (data === -1) { + $dialogContainer.html("
' . __('Authentication error') . '
'; ?>"); + } + else if (data) { + $dialogContainer.html("
' . __('The double autentication was deactivated successfully') . '
'; ?>"); + } + else { + $dialogContainer.html("
' . __('There was an error deactivating the double autentication') . '
'; ?>"); + } + }, + error: function(xhr, textStatus, errorThrown) { + $dialogContainer.html("
' . __('There was an error deactivating the double autentication') . '
'; ?>"); + } + }); + }); + + + $("div#dialog-double_auth").dialog({ + resizable: true, + draggable: true, + modal: true, + title: "", + overlay: { + opacity: 0.5, + background: "black" + }, + width: 300, + height: 150, + close: function(event, ui) { + // Abort the ajax request + if (typeof request != 'undefined') + request.abort(); + // Remove the contained html + $dialogContainer.empty(); + + document.location.reload(); + } + }) + .show(); +} From 0d54510462e8f7833e0d62f1b0580f848f5a9439 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 16:18:26 +0100 Subject: [PATCH 07/15] Modifications on the login process to include the 2 step verification --- pandora_console/general/login_page.php | 17 ++ pandora_console/index.php | 397 ++++++++++++++++--------- 2 files changed, 269 insertions(+), 145 deletions(-) diff --git a/pandora_console/general/login_page.php b/pandora_console/general/login_page.php index 1e18e4e005..e126d9268d 100644 --- a/pandora_console/general/login_page.php +++ b/pandora_console/general/login_page.php @@ -35,6 +35,7 @@ switch ($login_screen) { $logo_title = __('Go to Pandora FMS Website'); break; case 'logout': + case 'double_auth': $logo_link = 'index.php'; $logo_title = __('Go to Login'); break; @@ -129,6 +130,22 @@ echo ' echo __('Your session is over. Please close your browser window to close this Pandora session.').'

'; echo '

'; break; + case 'double_auth': + if (!empty ($page) && !empty ($sec)) { + foreach ($_POST as $key => $value) { + html_print_input_hidden ($key, $value); + } + } + echo ''; + echo ''; + echo ''; + break; default: if (isset($error_info)) { echo '

' . $error_info['title'] . '

'; diff --git a/pandora_console/index.php b/pandora_console/index.php index d054197723..e53f31354e 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -167,176 +167,283 @@ if (strlen($search) > 0) { $searchPage = true; } -// Login process -if (! isset ($config['id_user']) && isset ($_GET["login"])) { - include_once('include/functions_db.php'); //Include it to use escape_string_sql function - - $config["auth_error"] = ""; //Set this to the error message from the authorization mechanism - $nick = get_parameter_post ("nick"); //This is the variable with the login - $pass = get_parameter_post ("pass"); //This is the variable with the password - $nick = db_escape_string_sql($nick); - $pass = db_escape_string_sql($pass); - - // process_user_login is a virtual function which should be defined in each auth file. - // It accepts username and password. The rest should be internal to the auth file. - // The auth file can set $config["auth_error"] to an informative error output or reference their internal error messages to it - // process_user_login should return false in case of errors or invalid login, the nickname if correct - $nick_in_db = process_user_login ($nick, $pass); - - $expired_pass = false; - - if (($nick_in_db != false) && ((!is_user_admin($nick) - || $config['enable_pass_policy_admin'])) - && (defined('PANDORA_ENTERPRISE')) - && ($config['enable_pass_policy'])) { - include_once(ENTERPRISE_DIR . "/include/auth/mysql.php"); +// Login process +if (! isset ($config['id_user'])) { + if (isset ($_GET["login"])) { + include_once('include/functions_db.php'); //Include it to use escape_string_sql function - $blocked = login_check_blocked($nick); - - if ($blocked) { - require_once ('general/login_page.php'); - db_pandora_audit("Password expired", "Password expired: ".$nick, $nick); - while (@ob_end_flush ()); - exit (""); + $config["auth_error"] = ""; //Set this to the error message from the authorization mechanism + $nick = get_parameter_post ("nick"); //This is the variable with the login + $pass = get_parameter_post ("pass"); //This is the variable with the password + $nick = db_escape_string_sql($nick); + $pass = db_escape_string_sql($pass); + + //Since now, only the $pass variable are needed + unset ($_GET['pass'], $_POST['pass'], $_REQUEST['pass']); + + // If the auth_code exists, we assume the user has come through the double auth page + if (isset ($_POST['auth_code'])) { + $double_auth_success = false; + + // The double authentication is activated and the user has surpassed the first step (the login). + // Now the authentication code provided will be checked. + if (isset ($_SESSION['prepared_login_da'])) { + if (isset ($_SESSION['prepared_login_da']['id_user']) + && isset ($_SESSION['prepared_login_da']['timestamp'])) { + + $config["prepared_login_da"] = $_SESSION["prepared_login_da"]; + // The user has a maximum of 5 minutes to introduce the double auth code + $dauth_period = SECONDS_2MINUTES; + $now = time(); + $dauth_time = $config['prepared_login_da']['timestamp']; + + if ($now - $dauth_period < $dauth_time) { + // Nick + $nick = $config["prepared_login_da"]['id_user']; + // Code + $code = (string) get_parameter_post ("auth_code"); + + if (!empty($code)) { + $result = login_validate_double_auth_code($nick, $code); + + if ($result === true) { + // Double auth success + $double_auth_success = true; + } + else { + // Screen + $login_screen = 'double_auth'; + // Error message + $config["auth_error"] = __("Invalid code"); + } + } + else { + // Screen + $login_screen = 'double_auth'; + // Error message + $config["auth_error"] = __("The code shouldn't be empty"); + } + } + else { + // Expired login + unset ($_SESSION['prepared_login_da'], $config["prepared_login_da"]); + + // Error message + $config["auth_error"] = __('Expired login'); + } + } + else { + // If the code doesn't exist, remove the prepared login + unset ($_SESSION['prepared_login_da']); + + // Error message + $config["auth_error"] = __('Login error'); + } + } + // If $_SESSION['prepared_login_da'] doesn't exist, the user have to do the login again + else { + // Error message + $config["auth_error"] = __('Login error'); + } + + // Remove the authenticator code + unset ($_POST['auth_code'], $code); + + if (!$double_auth_success) { + $login_failed = true; + require_once ('general/login_page.php'); + db_pandora_audit("Logon Failed", "Invalid double auth login: " + .$_SESSION['remote_addr'], $_SESSION['remote_addr']); + while (@ob_end_flush ()); + exit (""); + } } - //Checks if password has expired - $check_status = check_pass_status($nick, $pass); - - switch ($check_status) { - case PASSSWORD_POLICIES_FIRST_CHANGE: //first change - case PASSSWORD_POLICIES_EXPIRED: //pass expired - $expired_pass = true; - login_change_password($nick); - break; + if (isset ($double_auth_success) && $double_auth_success) { + // This values are true cause there are checked before complete the 2nd auth step + $nick_in_db = $config["prepared_login_da"]['id_user']; + $expired_pass = false; } - } - - if (($nick_in_db !== false) && $expired_pass) { - //login ok and password has expired - - require_once ('general/login_page.php'); - db_pandora_audit("Password expired", - "Password expired: " . $nick, $nick); - while (@ob_end_flush ()); - exit (""); - } - else if (($nick_in_db !== false) && (!$expired_pass)) { - //login ok and password has not expired - $process_login = true; - - echo ""; - - unset ($_GET["sec2"]); - $_GET["sec"] = "general/logon_ok"; - $home_page =''; - if (isset($nick)) { - $user_info = users_get_user_by_id($nick); - $home_page = io_safe_output($user_info['section']); - $home_url = $user_info['data_section']; - if ($home_page != '') { - switch($home_page) { - case 'Event list': - $_GET["sec"] = "eventos"; - $_GET["sec2"] = "operation/events/events"; - break; - case 'Group view': - $_GET["sec"] = "estado"; - $_GET["sec2"] = "operation/agentes/group_view"; - break; - case 'Alert detail': - $_GET["sec"] = "estado"; - $_GET["sec2"] = "operation/agentes/alerts_status"; - break; - case 'Tactical view': - $_GET["sec"] = "estado"; - $_GET["sec2"] = "operation/agentes/tactical"; - break; - case 'Default': - $_GET["sec"] = "general/logon_ok"; - break; - case 'Dashboard': - $_GET["sec"] = "dashboard"; - $_GET["sec2"] = ENTERPRISE_DIR.'/dashboard/main_dashboard'; - break; - case 'Visual console': - $_GET["sec"] = "visualc"; - $_GET["sec2"] = "operation/visual_console/index"; - break; - case 'Other': - $home_url = io_safe_output($home_url); - parse_str ($home_url, $res); - $_GET["sec"] = $res["sec"]; - $_GET["sec2"] = $res["sec2"]; + else { + // process_user_login is a virtual function which should be defined in each auth file. + // It accepts username and password. The rest should be internal to the auth file. + // The auth file can set $config["auth_error"] to an informative error output or reference their internal error messages to it + // process_user_login should return false in case of errors or invalid login, the nickname if correct + $nick_in_db = process_user_login ($nick, $pass); + + $expired_pass = false; + + if (($nick_in_db != false) && ((!is_user_admin($nick) + || $config['enable_pass_policy_admin'])) + && (defined('PANDORA_ENTERPRISE')) + && ($config['enable_pass_policy'])) { + include_once(ENTERPRISE_DIR . "/include/auth/mysql.php"); + + $blocked = login_check_blocked($nick); + + if ($blocked) { + require_once ('general/login_page.php'); + db_pandora_audit("Password expired", "Password expired: ".$nick, $nick); + while (@ob_end_flush ()); + exit (""); + } + + //Checks if password has expired + $check_status = check_pass_status($nick, $pass); + + switch ($check_status) { + case PASSSWORD_POLICIES_FIRST_CHANGE: //first change + case PASSSWORD_POLICIES_EXPIRED: //pass expired + $expired_pass = true; + login_change_password($nick); break; } } - else { - $_GET["sec"] = "general/logon_ok"; - } - } - db_logon ($nick_in_db, $_SERVER['REMOTE_ADDR']); - $_SESSION['id_usuario'] = $nick_in_db; - $config['id_user'] = $nick_in_db; - //Remove everything that might have to do with people's passwords or logins - unset ($_GET['pass'], $pass, $_POST['pass'], $_REQUEST['pass'], $login_good); - - $user_language = get_user_language($config['id_user']); - - $l10n = NULL; - if (file_exists ('./include/languages/' . $user_language . '.mo')) { - $l10n = new gettext_reader (new CachedFileReader ('./include/languages/'.$user_language.'.mo')); - $l10n->load_tables(); - } - } - else { //login wrong - $blocked = false; - - if ((!is_user_admin($nick) || $config['enable_pass_policy_admin']) && defined('PANDORA_ENTERPRISE')) { - $blocked = login_check_blocked($nick); } - if (!$blocked) { - if (defined('PANDORA_ENTERPRISE')) { - login_check_failed($nick); //Checks failed attempts - } - $login_failed = true; + if (($nick_in_db !== false) && $expired_pass) { + //login ok and password has expired + require_once ('general/login_page.php'); - db_pandora_audit("Logon Failed", "Invalid login: ".$nick, $nick); + db_pandora_audit("Password expired", + "Password expired: " . $nick, $nick); while (@ob_end_flush ()); exit (""); } + else if (($nick_in_db !== false) && (!$expired_pass)) { + //login ok and password has not expired + + // Double auth check + if ((!isset ($double_auth_success) || !$double_auth_success) && login_is_double_auth_enabled($nick_in_db)) { + // Store this values in the session to know if the user login was correct + $_SESSION['prepared_login_da'] = array( + 'id_user' => $nick_in_db, + 'timestamp' => time() + ); + + // Load the page to introduce the double auth code + $login_screen = 'double_auth'; + require_once ('general/login_page.php'); + while (@ob_end_flush ()); + exit (""); + } + + //login ok and password has not expired + $process_login = true; + + echo ""; + + unset ($_GET["sec2"]); + $_GET["sec"] = "general/logon_ok"; + $home_page =''; + if (isset($nick)) { + $user_info = users_get_user_by_id($nick); + $home_page = io_safe_output($user_info['section']); + $home_url = $user_info['data_section']; + if ($home_page != '') { + switch($home_page) { + case 'Event list': + $_GET["sec"] = "eventos"; + $_GET["sec2"] = "operation/events/events"; + break; + case 'Group view': + $_GET["sec"] = "estado"; + $_GET["sec2"] = "operation/agentes/group_view"; + break; + case 'Alert detail': + $_GET["sec"] = "estado"; + $_GET["sec2"] = "operation/agentes/alerts_status"; + break; + case 'Tactical view': + $_GET["sec"] = "estado"; + $_GET["sec2"] = "operation/agentes/tactical"; + break; + case 'Default': + $_GET["sec"] = "general/logon_ok"; + break; + case 'Dashboard': + $_GET["sec"] = "dashboard"; + $_GET["sec2"] = ENTERPRISE_DIR.'/dashboard/main_dashboard'; + break; + case 'Visual console': + $_GET["sec"] = "visualc"; + $_GET["sec2"] = "operation/visual_console/index"; + break; + case 'Other': + $home_url = io_safe_output($home_url); + parse_str ($home_url, $res); + $_GET["sec"] = $res["sec"]; + $_GET["sec2"] = $res["sec2"]; + break; + } + } + else { + $_GET["sec"] = "general/logon_ok"; + } + } + db_logon ($nick_in_db, $_SERVER['REMOTE_ADDR']); + $_SESSION['id_usuario'] = $nick_in_db; + $config['id_user'] = $nick_in_db; + //Remove everything that might have to do with people's passwords or logins + unset ($pass, $login_good); + + $user_language = get_user_language($config['id_user']); + + $l10n = NULL; + if (file_exists ('./include/languages/' . $user_language . '.mo')) { + $l10n = new gettext_reader (new CachedFileReader ('./include/languages/'.$user_language.'.mo')); + $l10n->load_tables(); + } + } + else { //login wrong + $blocked = false; + + if ((!is_user_admin($nick) || $config['enable_pass_policy_admin']) && defined('PANDORA_ENTERPRISE')) { + $blocked = login_check_blocked($nick); + } + + if (!$blocked) { + if (defined('PANDORA_ENTERPRISE')) { + login_check_failed($nick); //Checks failed attempts + } + $login_failed = true; + require_once ('general/login_page.php'); + db_pandora_audit("Logon Failed", "Invalid login: ".$nick, $nick); + while (@ob_end_flush ()); + exit (""); + } + else { + require_once ('general/login_page.php'); + db_pandora_audit("Logon Failed", "Invalid login: ".$nick, $nick); + while (@ob_end_flush ()); + exit (""); + } + } + } + // Hash login process + elseif (isset ($_GET["loginhash"])) { + $loginhash_data = get_parameter("loginhash_data", ""); + $loginhash_user = str_rot13(get_parameter("loginhash_user", "")); + + if ($config["loginhash_pwd"] != "" && $loginhash_data == md5($loginhash_user.$config["loginhash_pwd"])) { + db_logon ($loginhash_user, $_SERVER['REMOTE_ADDR']); + $_SESSION['id_usuario'] = $loginhash_user; + $config["id_user"] = $loginhash_user; + } else { require_once ('general/login_page.php'); - db_pandora_audit("Logon Failed", "Invalid login: ".$nick, $nick); + db_pandora_audit("Logon Failed (loginhash", "", "system"); while (@ob_end_flush ()); exit (""); } } -} -// Hash login process -elseif (! isset ($config['id_user']) && isset ($_GET["loginhash"])) { - $loginhash_data = get_parameter("loginhash_data", ""); - $loginhash_user = str_rot13(get_parameter("loginhash_user", "")); - - if ($config["loginhash_pwd"] != "" && $loginhash_data == md5($loginhash_user.$config["loginhash_pwd"])) { - db_logon ($loginhash_user, $_SERVER['REMOTE_ADDR']); - $_SESSION['id_usuario'] = $loginhash_user; - $config["id_user"] = $loginhash_user; - } + // There is no user connected else { require_once ('general/login_page.php'); - db_pandora_audit("Logon Failed (loginhash", "", "system"); while (@ob_end_flush ()); exit (""); } } -// There is no user connected -elseif (! isset ($config['id_user'])) { - require_once ('general/login_page.php'); - while (@ob_end_flush ()); - exit (""); -} // Log off if (isset ($_GET["bye"])) { From 30f24ff5837f3342e1540a1ff1d3eab15f89aaf2 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 17:00:55 +0100 Subject: [PATCH 08/15] Modifications on the ajax calls to gain compatibility --- .../include/ajax/double_auth.ajax.php | 8 ++++---- pandora_console/operation/users/user_edit.php | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pandora_console/include/ajax/double_auth.ajax.php b/pandora_console/include/ajax/double_auth.ajax.php index 770749e683..642ceb778a 100644 --- a/pandora_console/include/ajax/double_auth.ajax.php +++ b/pandora_console/include/ajax/double_auth.ajax.php @@ -225,7 +225,7 @@ if ($get_double_auth_info_page) { $("#"+containerID).html("/images/spinner.gif\" />"); $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'html', data: { @@ -320,7 +320,7 @@ if ($get_double_auth_generation_page) { $("#"+containerID).html("/images/spinner.gif\" />"); $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'html', data: { @@ -364,7 +364,7 @@ if ($get_double_auth_generation_page) { $("#"+containerID).html("/images/spinner.gif\" />"); $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'html', data: { @@ -458,7 +458,7 @@ if ($get_double_auth_validation_page) { $("div#button-container").find("img").show(); $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'json', data: { diff --git a/pandora_console/operation/users/user_edit.php b/pandora_console/operation/users/user_edit.php index 3068cd3f9f..c3fa1f11b9 100644 --- a/pandora_console/operation/users/user_edit.php +++ b/pandora_console/operation/users/user_edit.php @@ -520,7 +520,7 @@ function show_double_auth_info () { // Load the info page var request = $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'html', data: { @@ -539,14 +539,14 @@ function show_double_auth_info () { } // data is a number, convert it to integer to do the compare else if (Number(data) === -1) { - $dialogContainer.html(""); + $dialogContainer.html("
' . __('Authentication error') . '
'; ?>"); } else { - $dialogContainer.html(""); + $dialogContainer.html("
' . __('Error') . '
'; ?>"); } }, error: function(xhr, textStatus, errorThrown) { - $dialogContainer.html(""); + $dialogContainer.html("
' . __('There was an error loading the data') . '
'; ?>"); } }); @@ -585,7 +585,7 @@ function show_double_auth_activation () { // Load the info page var request = $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'html', data: { @@ -604,14 +604,14 @@ function show_double_auth_activation () { } // data is a number, convert it to integer to do the compare else if (Number(data) === -1) { - $dialogContainer.html(""); + $dialogContainer.html("
' . __('Authentication error') . '
'; ?>"); } else { - $dialogContainer.html(""); + $dialogContainer.html("
' . __('Error') . '
'; ?>"); } }, error: function(xhr, textStatus, errorThrown) { - $dialogContainer.html(""); + $dialogContainer.html("
' . __('There was an error loading the data') . '
'; ?>"); } }); @@ -662,7 +662,7 @@ function show_double_auth_deactivation () { // Deactivate the double auth request = $.ajax({ - url: 'ajax.php', + url: "", type: 'POST', dataType: 'json', data: { From 9512216ed66ec91b4e3a607d59888b132ad6b648 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 18:19:47 +0100 Subject: [PATCH 09/15] Moved some functions --- pandora_console/include/functions.php | 53 ++++++++++++++++++++++++++- pandora_console/index.php | 4 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/pandora_console/include/functions.php b/pandora_console/include/functions.php index 0b28d59cee..a059d60399 100644 --- a/pandora_console/include/functions.php +++ b/pandora_console/include/functions.php @@ -2225,4 +2225,55 @@ function print_audit_csv ($data) { } } -?> +/** + * Validate the code given to surpass the 2 step authentication + * + * @param string User name + * @param string Code given by the authenticator app + * + * @return -1 if the parameters introduced are incorrect, + * there is a problem accessing the user secret or + * if an exception are launched. + * true if the code is valid. + * false if the code is invalid. + */ +function validate_double_auth_code ($user, $code) { + global $config; + require_once ($config['homedir'].'/include/auth/GAuth/Auth.php'); + $result = false; + + if (empty($user) || empty($code)) { + $result = -1; + } + else { + $secret = db_get_value('secret', 'tuser_double_auth', 'id_user', $user); + + if ($secret === false) { + $result = -1; + } + else if (!empty($secret)) { + try { + $gAuth = new \GAuth\Auth($secret); + $result = $gAuth->validateCode($code); + } catch (Exception $e) { + $result = -1; + } + } + } + + return $result; +} + +/** + * Get if the 2 step authentication is enabled for the user given + * + * @param string User name + * + * @return true if the user has the double auth enabled or false otherwise. + */ +function is_double_auth_enabled ($user) { + $result = (bool) db_get_value('id', 'tuser_double_auth', 'id_user', $user); + + return $result; +} +?> \ No newline at end of file diff --git a/pandora_console/index.php b/pandora_console/index.php index e53f31354e..dbbd51a439 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -204,7 +204,7 @@ if (! isset ($config['id_user'])) { $code = (string) get_parameter_post ("auth_code"); if (!empty($code)) { - $result = login_validate_double_auth_code($nick, $code); + $result = validate_double_auth_code($nick, $code); if ($result === true) { // Double auth success @@ -314,7 +314,7 @@ if (! isset ($config['id_user'])) { //login ok and password has not expired // Double auth check - if ((!isset ($double_auth_success) || !$double_auth_success) && login_is_double_auth_enabled($nick_in_db)) { + if ((!isset ($double_auth_success) || !$double_auth_success) && is_double_auth_enabled($nick_in_db)) { // Store this values in the session to know if the user login was correct $_SESSION['prepared_login_da'] = array( 'id_user' => $nick_in_db, From 716a52e94d5d53913b7c603518f1479af53eb578 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 18:43:16 +0100 Subject: [PATCH 10/15] Fixed some paths --- pandora_console/general/login_page.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pandora_console/general/login_page.php b/pandora_console/general/login_page.php index e126d9268d..023ea4c022 100644 --- a/pandora_console/general/login_page.php +++ b/pandora_console/general/login_page.php @@ -199,9 +199,16 @@ if (isset ($login_failed)) { echo ''; } -ui_require_css_file ('dialog'); -ui_require_css_file ('jquery-ui-1.10.0.custom'); -ui_require_jquery_file('jquery-ui-1.10.0.custom'); +if (defined ('METACONSOLE')) { + ui_require_css_file ('../../dialog'); + ui_require_css_file ('../../jquery-ui-1.10.0.custom'); + ui_require_jquery_file('../../jquery-ui-1.10.0.custom'); +} +else { + ui_require_css_file ('dialog'); + ui_require_css_file ('jquery-ui-1.10.0.custom'); + ui_require_jquery_file('jquery-ui-1.10.0.custom'); +} ?> Date: Thu, 11 Dec 2014 18:43:30 +0100 Subject: [PATCH 11/15] Minor fixes --- pandora_console/index.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pandora_console/index.php b/pandora_console/index.php index dbbd51a439..56531c8dd9 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -191,15 +191,14 @@ if (! isset ($config['id_user'])) { if (isset ($_SESSION['prepared_login_da']['id_user']) && isset ($_SESSION['prepared_login_da']['timestamp'])) { - $config["prepared_login_da"] = $_SESSION["prepared_login_da"]; // The user has a maximum of 5 minutes to introduce the double auth code $dauth_period = SECONDS_2MINUTES; $now = time(); - $dauth_time = $config['prepared_login_da']['timestamp']; + $dauth_time = $_SESSION['prepared_login_da']['timestamp']; if ($now - $dauth_period < $dauth_time) { // Nick - $nick = $config["prepared_login_da"]['id_user']; + $nick = $_SESSION["prepared_login_da"]['id_user']; // Code $code = (string) get_parameter_post ("auth_code"); @@ -215,6 +214,10 @@ if (! isset ($config['id_user'])) { $login_screen = 'double_auth'; // Error message $config["auth_error"] = __("Invalid code"); + + if (!isset($_SESSION['prepared_login_da']['attempts'])) + $_SESSION['prepared_login_da']['attempts'] = 0; + $_SESSION['prepared_login_da']['attempts']++; } } else { @@ -222,11 +225,15 @@ if (! isset ($config['id_user'])) { $login_screen = 'double_auth'; // Error message $config["auth_error"] = __("The code shouldn't be empty"); + + if (!isset($_SESSION['prepared_login_da']['attempts'])) + $_SESSION['prepared_login_da']['attempts'] = 0; + $_SESSION['prepared_login_da']['attempts']++; } } else { // Expired login - unset ($_SESSION['prepared_login_da'], $config["prepared_login_da"]); + unset ($_SESSION['prepared_login_da']); // Error message $config["auth_error"] = __('Expired login'); @@ -253,7 +260,7 @@ if (! isset ($config['id_user'])) { $login_failed = true; require_once ('general/login_page.php'); db_pandora_audit("Logon Failed", "Invalid double auth login: " - .$_SESSION['remote_addr'], $_SESSION['remote_addr']); + .$_SERVER['REMOTE_ADDR'], $_SERVER['REMOTE_ADDR']); while (@ob_end_flush ()); exit (""); } @@ -318,7 +325,8 @@ if (! isset ($config['id_user'])) { // Store this values in the session to know if the user login was correct $_SESSION['prepared_login_da'] = array( 'id_user' => $nick_in_db, - 'timestamp' => time() + 'timestamp' => time(), + 'attempts' => 0 ); // Load the page to introduce the double auth code From 5e80428ed9f3ea0f30b2d183bdcb80714ff5aa56 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Thu, 11 Dec 2014 18:49:04 +0100 Subject: [PATCH 12/15] Revert "Fixed some paths" This reverts commit 7d1894e2ca985b9fad22c8cbff5373c17f2e6909. --- pandora_console/general/login_page.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/pandora_console/general/login_page.php b/pandora_console/general/login_page.php index 023ea4c022..e126d9268d 100644 --- a/pandora_console/general/login_page.php +++ b/pandora_console/general/login_page.php @@ -199,16 +199,9 @@ if (isset ($login_failed)) { echo ''; } -if (defined ('METACONSOLE')) { - ui_require_css_file ('../../dialog'); - ui_require_css_file ('../../jquery-ui-1.10.0.custom'); - ui_require_jquery_file('../../jquery-ui-1.10.0.custom'); -} -else { - ui_require_css_file ('dialog'); - ui_require_css_file ('jquery-ui-1.10.0.custom'); - ui_require_jquery_file('jquery-ui-1.10.0.custom'); -} +ui_require_css_file ('dialog'); +ui_require_css_file ('jquery-ui-1.10.0.custom'); +ui_require_jquery_file('jquery-ui-1.10.0.custom'); ?> Date: Fri, 12 Dec 2014 12:38:08 +0100 Subject: [PATCH 13/15] Fixed the 2 step login --- pandora_console/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandora_console/index.php b/pandora_console/index.php index 56531c8dd9..335b283dc9 100755 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -268,7 +268,7 @@ if (! isset ($config['id_user'])) { if (isset ($double_auth_success) && $double_auth_success) { // This values are true cause there are checked before complete the 2nd auth step - $nick_in_db = $config["prepared_login_da"]['id_user']; + $nick_in_db = $_SESSION["prepared_login_da"]['id_user']; $expired_pass = false; } else { @@ -319,7 +319,7 @@ if (! isset ($config['id_user'])) { } else if (($nick_in_db !== false) && (!$expired_pass)) { //login ok and password has not expired - + // Double auth check if ((!isset ($double_auth_success) || !$double_auth_success) && is_double_auth_enabled($nick_in_db)) { // Store this values in the session to know if the user login was correct From 220466fa1feca43312d89becd493803ad2b75cb7 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Mon, 15 Dec 2014 13:47:33 +0100 Subject: [PATCH 14/15] Added the double auth functionality to the mobile version --- pandora_console/mobile/include/user.class.php | 227 +++++++++++++----- pandora_console/mobile/index.php | 72 +++++- 2 files changed, 237 insertions(+), 62 deletions(-) diff --git a/pandora_console/mobile/include/user.class.php b/pandora_console/mobile/include/user.class.php index b79818e5c1..50da3360ea 100644 --- a/pandora_console/mobile/include/user.class.php +++ b/pandora_console/mobile/include/user.class.php @@ -19,22 +19,12 @@ class User { private $user; private $logged = false; private $errorLogin = false; + private $loginTime = false; private $logout_action = false; + private $needDoubleAuth = false; + private $errorDoubleAuth = false; - public function __construct($user = null, $password = null) { - $this->user = $user; - $this->errorLogin = false; - - if (process_user_login($this->user, $password)) { - $this->logged = true; - $this->hackInjectConfig(); - } - else { - $this->logged = false; - } - } - - public static function getInstance() { + public static function getInstance () { if (!(self::$instance instanceof self)) { //Check if in the session $system = System::getInstance(); @@ -51,22 +41,22 @@ class User { return self::$instance; } - public function hackInjectConfig() { - //hack to compatibility with pandora + public function saveLogin () { if ($this->logged) { - global $config; - $system = System::getInstance(); - - $config['id_user'] = $this->user; - - $system->setSessionBase('id_usuario', $this->user); $system->setSession('user', $this); + + if (!$this->needDoubleAuth) { + //hack to compatibility with pandora + global $config; + $config['id_user'] = $this->user; + $system->setSessionBase('id_usuario', $this->user); + } } } - public function isLogged() { + public function isLogged () { $system = System::getInstance(); $autologin = $system->getRequest('autologin', false); @@ -74,15 +64,13 @@ class User { $user = $system->getRequest('user', null); $password = $system->getRequest('password', null); - if ($this->checkLogin($user, $password)) { - $this->hackInjectConfig(); - } + $this->login($user, $password); } return $this->logged; } - - public function checkLogin($user = null, $password = null) { + + public function login ($user = null, $password = null) { $system = System::getInstance(); if (($user == null) && ($password == null)) { @@ -92,44 +80,126 @@ class User { } if (!empty($user) && !empty($password)) { - if (process_user_login($user, $password) !== false) { + $user_in_db = process_user_login($user, $password); + if ($user_in_db !== false) { $this->logged = true; - $this->user = $user; + $this->user = $user_in_db; + $this->loginTime = time(); $this->errorLogin = false; + + // The user login was successful, but the second step is not completed + if ($this->isDobleAuthRequired()) { + $this->needDoubleAuth = true; + } } else { $this->logged = false; + $this->loginTime = false; $this->errorLogin = true; + $this->needDoubleAuth = false; + $this->errorDoubleAuth = false; } } - if ($this->logged) { - $this->hackInjectConfig(); - - if (! check_acl($system->getConfig('id_user'), 0, "AR")) { - db_pandora_audit("ACL Violation", - "Trying to access Agent Data view"); - require ("../general/noaccess.php"); - return; - } - } + $this->saveLogin(); return $this->logged; } - - public function logout() { - $this->user = null; - $this->logged = false; - $this->errorLogin = false; - $this->logout_action = true; - - $system = System::getInstance(); - $system->setSession('user', null); + + public function getLoginTime () { + return $this->loginTime; } - public function showLogin() { + public function isWaitingDoubleAuth () { + return $this->needDoubleAuth; + } + + public function isDobleAuthRequired ($user = false) { + if (empty($user) && !empty($this->user)) + $user = $this->user; + + if (!empty($user)) + return (bool) db_get_value('id', 'tuser_double_auth', 'id_user', $user); + else + return false; + } + + public function validateDoubleAuthCode ($user = null, $code = null) { + + if (!$this->needDoubleAuth) { + return true; + } + + $system = System::getInstance(); + require_once ($system->getConfig('homedir').'/include/auth/GAuth/Auth.php'); + + $result = false; + + if (empty($user)) { + $user = $this->user; + } + if (empty($code)) { + $code = $system->getRequest('auth_code', null); + } + + if (!empty($user) && !empty($code)) { + $secret = db_get_value('secret', 'tuser_double_auth', 'id_user', $user); + + if ($secret === false) { + $result = false; + $this->errorDoubleAuth = array( + 'title_text' => __('Double authentication failed'), + 'content_text' => __('Secret code not found') .". " + .__('Please contact the administrator to reset your double authentication') + ); + } + else if (!empty($secret)) { + try { + $gAuth = new \GAuth\Auth($secret); + $result = $gAuth->validateCode($code); + + // Double auth success + if ($result) { + $this->needDoubleAuth = false; + $this->saveLogin(); + } + else { + $result = false; + $this->errorDoubleAuth = array( + 'title_text' => __('Double authentication failed'), + 'content_text' => __('Invalid code') + ); + } + } catch (Exception $e) { + $result = false; + $this->errorDoubleAuth = array( + 'title_text' => __('Double authentication failed'), + 'content_text' => __('There was an error checking the code') + ); + } + } + } + + return $result; + } + + public function logout () { + $this->user = null; + $this->logged = false; + $this->loginTime = false; + $this->errorLogin = false; + $this->logout_action = true; + $this->needDoubleAuth = false; + $this->errorDoubleAuth = false; + + $system = System::getInstance(); + $system->setSession('user', null); + $system->sessionDestroy(); + } + + public function showLoginPage () { global $pandora_version; $ui = Ui::getInstance(); @@ -191,16 +261,65 @@ class User { $this->errorLogin = false; $this->logout_action = false; } - - public function getIdUser() { + + public function showDoubleAuthPage () { + global $pandora_version; + + $ui = Ui::getInstance(); + + $ui->createPage(); + if (!empty($this->errorDoubleAuth)) { + $options['type'] = 'onStart'; + $options['title_text'] = $this->errorDoubleAuth['title_text']; + $options['content_text'] = $this->errorDoubleAuth['content_text'] . "
"; + $ui->addDialog($options); + } + $left_button = $ui->createHeaderButton( + array('icon' => 'back', + 'pos' => 'left', + 'text' => __('Logout'), + 'href' => 'index.php?action=logout')); + $ui->createHeader('', $left_button); + $ui->showFooter(false); + $ui->beginContent(); + $ui->contentAddHtml(''); + $ui->contentAddHtml('
'); + $ui->beginForm(); + $ui->formAddHtml(html_print_input_hidden('action', 'double_auth', true)); + $options = array( + 'name' => 'auth_code', + 'value' => '', + 'placeholder' => __('Authenticator code'), + 'label' => __('Authenticator code') + ); + $ui->formAddInputPassword($options); + $options = array( + 'value' => __('Check code'), + 'icon' => 'arrow-r', + 'icon_pos' => 'right', + 'name' => 'auth_code_btn' + ); + $ui->formAddSubmitButton($options); + $ui->endForm(); + $ui->contentAddHtml('
'); + $ui->endContent(); + $ui->showPage(); + + $this->errorDoubleAuth = false; + } + + public function getIdUser () { return $this->user; //Oldies methods } - public function isInGroup($access = "AR", $id_group = 0, $name_group = false) { + public function isInGroup ($access = "AR", $id_group = 0, $name_group = false) { return (bool)check_acl($this->user, $id_group, $access); } - public function getIdGroups($access = "AR", $all = false) { + public function getIdGroups ($access = "AR", $all = false) { return array_keys(users_get_groups($this->user, $access, $all)); } } diff --git a/pandora_console/mobile/index.php b/pandora_console/mobile/index.php index a2fe7a5e3f..7a7f5c0414 100644 --- a/pandora_console/mobile/index.php +++ b/pandora_console/mobile/index.php @@ -43,13 +43,32 @@ $enterpriseHook = enterprise_include('mobile/operation/home.php'); $system = System::getInstance(); +require_once($system->getConfig('homedir').'/include/constants.php'); + $user = User::getInstance(); -$user->hackInjectConfig(); +$user->saveLogin(); $page = $system->getRequest('page', 'home'); $action = $system->getRequest('action'); -if (!$user->isLogged()) { - $action = 'login'; + +// The logout action has priority +if ($action != 'logout') { + if (!$user->isLogged()) { + $action = 'login'; + } + else if ($user->isWaitingDoubleAuth()) { + $dauth_period = SECONDS_2MINUTES; + $now = time(); + $dauth_time = $user->getLoginTime(); + + if ($now - $dauth_period < $dauth_time) { + $action = 'double_auth'; + } + // Expired login + else { + $action = 'logout'; + } + } } if ($action != "ajax") { @@ -107,11 +126,45 @@ switch ($action) { return; break; case 'login': - if (!$user->checkLogin()) { - $user->showLogin(); + if ($user->login() && $user->isLogged()) { + if ($user->isWaitingDoubleAuth()) { + if ($user->validateDoubleAuthCode()) { + $user_language = get_user_language ($system->getConfig('id_user')); + if (file_exists ('../include/languages/'.$user_language.'.mo')) { + $l10n = new gettext_reader (new CachedFileReader('../include/languages/'.$user_language.'.mo')); + $l10n->load_tables(); + } + if (class_exists("HomeEnterprise")) + $home = new HomeEnterprise(); + else + $home = new Home(); + $home->show(); + } + else { + $user->showDoubleAuthPage(); + } + } + else { + $user_language = get_user_language ($system->getConfig('id_user')); + if (file_exists ('../include/languages/'.$user_language.'.mo')) { + $l10n = new gettext_reader (new CachedFileReader('../include/languages/'.$user_language.'.mo')); + $l10n->load_tables(); + } + if (class_exists("HomeEnterprise")) + $home = new HomeEnterprise(); + else + $home = new Home(); + $home->show(); + } + } else { - if ($user->isLogged()) { + $user->showLoginPage(); + } + break; + case 'double_auth': + if ($user->isLogged()) { + if ($user->validateDoubleAuthCode()) { $user_language = get_user_language ($system->getConfig('id_user')); if (file_exists ('../include/languages/'.$user_language.'.mo')) { $l10n = new gettext_reader (new CachedFileReader('../include/languages/'.$user_language.'.mo')); @@ -124,13 +177,16 @@ switch ($action) { $home->show(); } else { - $user->showLoginFail(); + $user->showDoubleAuthPage(); } } + else { + $user->showLoginPage(); + } break; case 'logout': $user->logout(); - $user->showLogin(); + $user->showLoginPage(); break; default: if (class_exists("Enterprise")) { From adbb1a6f122810070169b7a10b54edbdd42f15c8 Mon Sep 17 00:00:00 2001 From: Alejandro Gallardo Escobar Date: Mon, 15 Dec 2014 14:59:22 +0100 Subject: [PATCH 15/15] Added a new command to disable the double authentication for the specified user --- pandora_server/util/pandora_manage.pl | 40 +++++++++++++++++++++------ 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/pandora_server/util/pandora_manage.pl b/pandora_server/util/pandora_manage.pl index a092cbe70e..202a651a24 100644 --- a/pandora_server/util/pandora_manage.pl +++ b/pandora_server/util/pandora_manage.pl @@ -154,6 +154,7 @@ sub help_screen{ help_screen_line('--add_profile_to_user', ' []', 'Add a profile in group to a user'); help_screen_line('--disable_eacl', '', 'Disable enterprise ACL system'); help_screen_line('--enable_eacl', '', 'Enable enterprise ACL system'); + help_screen_line('--disable_double_auth', '', 'Disable the double authentication for the specified user'); print "\nEVENTS:\n\n" unless $param ne ''; help_screen_line('--create_event', " [ \n\t \n\t ]", 'Add event'); help_screen_line('--validate_event', " \n\t ", 'Validate events'); @@ -3362,6 +3363,23 @@ sub cli_enable_eacl ($$) { exit; } +############################################################################### +# Disable double authentication +# Related option: --disable_double_auth +############################################################################### +sub cli_disable_double_auth () { + my $user_id = @ARGV[2]; + + print_log "[INFO] Disabling double authentication for the user '$user_id'\n\n"; + + $user_id = safe_input($user_id); + + # Delete the user secret + my $result = db_do ($dbh, 'DELETE FROM tuser_double_auth WHERE id_user = ?', $user_id); + + exit; +} + ############################################################################### # Enable user # Related option: --enable_user @@ -3691,21 +3709,25 @@ sub pandora_manage_main ($$$) { } elsif ($param eq '--disable_alerts') { param_check($ltotal, 0); - cli_disable_alerts ($conf, $dbh); - } + cli_disable_alerts ($conf, $dbh); + } elsif ($param eq '--enable_alerts') { param_check($ltotal, 0); - cli_enable_alerts ($conf, $dbh); - } + cli_enable_alerts ($conf, $dbh); + } elsif ($param eq '--disable_eacl') { param_check($ltotal, 0); - cli_disable_eacl ($conf, $dbh); - } + cli_disable_eacl ($conf, $dbh); + } elsif ($param eq '--enable_eacl') { param_check($ltotal, 0); - cli_enable_eacl ($conf, $dbh); - } - elsif ($param eq '--disable_group') { + cli_enable_eacl ($conf, $dbh); + } + elsif ($param eq '--disable_double_auth') { + param_check($ltotal, 1); + cli_disable_double_auth(); + } + elsif ($param eq '--disable_group') { param_check($ltotal, 1); cli_disable_group(); }