From f8ea67f2bc59d46290b85b2e578c649655f000e7 Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Fri, 16 Sep 2016 13:51:34 +0530 Subject: [PATCH 1/2] Fix #12530: Show enabled links for Edit and Execute after proper priv checks Signed-off-by: Deven Bansod --- ChangeLog | 1 + js/messages.php | 2 ++ js/rte.js | 6 +++++ libraries/DatabaseInterface.php | 5 ++-- libraries/rte/rte_export.lib.php | 26 +++++++++++++----- libraries/rte/rte_list.lib.php | 42 +++++++++++++++++++++++------- libraries/rte/rte_routines.lib.php | 41 ++++++++++++++++++++--------- libraries/rte/rte_words.lib.php | 6 +++++ 8 files changed, 97 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index 703d330f71d3..beea941cc0c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -30,6 +30,7 @@ phpMyAdmin - ChangeLog - issue #12473 Code can throw unhandled exception - issue #12550 Do not try to keep alive session even after expiry - issue #12512 Fixed rendering BBCode links in setup +- issue #12530 "Edit routine" crashes when the current user is not the definer, even if privileges are adequate 4.6.4 (2016-08-16) - issue [security] Weaknesses with cookie encryption, see PMASA-2016-29 diff --git a/js/messages.php b/js/messages.php index 9b8ecf2b3361..c79629dd1cb6 100644 --- a/js/messages.php +++ b/js/messages.php @@ -385,6 +385,8 @@ function () { $js_messages['MissingReturn'] = __('The definition of a stored function must contain a RETURN statement!'); $js_messages['strExport'] = __('Export'); +$js_messages['NoExportable'] + = __('No routine is exportable. Required privileges may be lacking.'); /* For ENUM/SET editor*/ $js_messages['enum_editor'] = __('ENUM/SET editor'); diff --git a/js/rte.js b/js/rte.js index ca2f6d4d6c5a..9a189aeae39a 100644 --- a/js/rte.js +++ b/js/rte.js @@ -128,6 +128,11 @@ RTE.COMMON = { var count = export_anchors.length; var returnCount = 0; + // No routine is exportable (due to privilege issues) + if (count === 0) { + PMA_ajaxShowMessage(PMA_messages.NoExportable); + } + export_anchors.each(function () { $.get($(this).attr('href'), {'ajax_request': true}, function (data) { returnCount++; @@ -149,6 +154,7 @@ RTE.COMMON = { } else { $.get($this.attr('href'), {'ajax_request': true}, showExport); } + PMA_ajaxRemoveMessage($msg); function showExport(data) { if (data.success === true) { diff --git a/libraries/DatabaseInterface.php b/libraries/DatabaseInterface.php index db86343fb3dc..72fd3e42c7db 100644 --- a/libraries/DatabaseInterface.php +++ b/libraries/DatabaseInterface.php @@ -1824,10 +1824,11 @@ public function getProceduresOrFunctions($db, $which, $link = null) * @param string $db db name * @param string $which PROCEDURE | FUNCTION | EVENT | VIEW * @param string $name the procedure|function|event|view name + * @param object $link MySQL link * * @return string the definition */ - public function getDefinition($db, $which, $name) + public function getDefinition($db, $which, $name, $link = null) { $returned_field = array( 'PROCEDURE' => 'Create Procedure', @@ -1838,7 +1839,7 @@ public function getDefinition($db, $which, $name) $query = 'SHOW CREATE ' . $which . ' ' . Util::backquote($db) . '.' . Util::backquote($name); - return($this->fetchValue($query, 0, $returned_field[$which])); + return($this->fetchValue($query, 0, $returned_field[$which], $link)); } /** diff --git a/libraries/rte/rte_export.lib.php b/libraries/rte/rte_export.lib.php index 55f9c2da57eb..19fa6dd0fb15 100644 --- a/libraries/rte/rte_export.lib.php +++ b/libraries/rte/rte_export.lib.php @@ -44,15 +44,17 @@ function PMA_RTE_handleExport($export_data) } else { $_db = htmlspecialchars(PMA\libraries\Util::backquote($db)); $message = __('Error in processing request:') . ' ' - . sprintf(PMA_RTE_getWord('not_found'), $item_name, $_db); - $response = Message::error($message); + . sprintf(PMA_RTE_getWord('not_found'), $item_name, $_db) + . ' OR ' . PMA_RTE_getWord('no_view'); + $message = Message::error($message); + if ($GLOBALS['is_ajax_request'] == true) { $response = PMA\libraries\Response::getInstance(); $response->setRequestStatus(false); $response->addJSON('message', $message); exit; } else { - $response->display(); + $message->display(); } } } // end PMA_RTE_handleExport() @@ -70,6 +72,9 @@ function PMA_EVN_handleExport() if (! empty($_GET['export_item']) && ! empty($_GET['item_name'])) { $item_name = $_GET['item_name']; $export_data = $GLOBALS['dbi']->getDefinition($db, 'EVENT', $item_name); + if (! $export_data) { + $export_data = false; + } PMA_RTE_handleExport($export_data); } } // end PMA_EVN_handleExport() @@ -89,13 +94,20 @@ function PMA_RTN_handleExport() && ! empty($_GET['item_type']) ) { if ($_GET['item_type'] == 'FUNCTION' || $_GET['item_type'] == 'PROCEDURE') { - $export_data = "DELIMITER $$\n" - . $GLOBALS['dbi']->getDefinition( + $rtn_definition + = $GLOBALS['dbi']->getDefinition( $db, $_GET['item_type'], $_GET['item_name'] - ) - . "$$\nDELIMITER ;\n"; + ); + if (! $rtn_definition) { + $export_data = false; + } else { + $export_data = "DELIMITER $$\n" + . $rtn_definition + . "$$\nDELIMITER ;\n"; + } + PMA_RTE_handleExport($export_data); } } diff --git a/libraries/rte/rte_list.lib.php b/libraries/rte/rte_list.lib.php index 7d08d984b736..85d27d6b541a 100644 --- a/libraries/rte/rte_list.lib.php +++ b/libraries/rte/rte_list.lib.php @@ -196,9 +196,24 @@ function PMA_RTN_getRowForList($routine, $rowclass = '') $retval .= " \n"; $retval .= " \n"; $retval .= " \n"; + + // this is for our purpose to decide whether to + // show the edit link or not, so we need the DEFINER for the routine + $where = "ROUTINE_SCHEMA " . PMA\libraries\Util::getCollateForIS() . "=" + . "'" . PMA\libraries\Util::sqlAddSlashes($db) . "' " + . "AND SPECIFIC_NAME='" . PMA\libraries\Util::sqlAddSlashes($routine['name']) . "'" + . "AND ROUTINE_TYPE='" . PMA\libraries\Util::sqlAddSlashes($routine['type']) . "'"; + $query = "SELECT `DEFINER` FROM INFORMATION_SCHEMA.ROUTINES WHERE $where;"; + $routine_definer = $GLOBALS['dbi']->fetchValue($query, 0, 0, $GLOBALS['controllink']); + + $curr_user = $GLOBALS['dbi']->getCurrentUser(); + // Since editing a procedure involved dropping and recreating, check also for // CREATE ROUTINE privilege to avoid lost procedures. - if (PMA\libraries\Util::currentUserHasPrivilege('CREATE ROUTINE', $db)) { + if ((PMA\libraries\Util::currentUserHasPrivilege('CREATE ROUTINE', $db) + && $curr_user == $routine_definer) + || $GLOBALS['is_superuser'] + ) { $retval .= ' \n"; $retval .= " \n"; - $retval .= ' ' . $titles['Export'] . "\n"; + if ((PMA\libraries\Util::currentUserHasPrivilege('CREATE ROUTINE', $db) + && $curr_user == $routine_definer) + || $GLOBALS['is_superuser'] + ) { + $retval .= ' ' . $titles['Export'] . "\n"; + } else { + $retval .= " {$titles['NoExport']}\n"; + } $retval .= " \n"; $retval .= " \n"; $retval .= ' fetchSingleRow($query); + $routine = $GLOBALS['dbi']->fetchSingleRow($query, 'ASSOC', $link); if (! $routine) { return false; @@ -595,13 +604,19 @@ function PMA_RTN_getDataFromName($name, $type, $all = true) $retval['item_name'] = $routine['SPECIFIC_NAME']; $retval['item_type'] = $routine['ROUTINE_TYPE']; - $parser = new SqlParser\Parser( - $GLOBALS['dbi']->getDefinition( + $definition + = $GLOBALS['dbi']->getDefinition( $db, $routine['ROUTINE_TYPE'], - $routine['SPECIFIC_NAME'] - ) - ); + $routine['SPECIFIC_NAME'], + $link + ); + + if ($definition == NULL) { + return false; + } + + $parser = new SqlParser\Parser($definition); /** * @var CreateStatement $stmt @@ -1283,7 +1298,7 @@ function PMA_RTN_handleExecute() if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['item_name'])) { // Build the queries $routine = PMA_RTN_getDataFromName( - $_REQUEST['item_name'], $_REQUEST['item_type'], false + $_REQUEST['item_name'], $_REQUEST['item_type'], false, true ); if ($routine === false) { $message = __('Error in processing request:') . ' '; @@ -1481,7 +1496,7 @@ function PMA_RTN_handleExecute() * Display the execute form for a routine. */ $routine = PMA_RTN_getDataFromName( - $_GET['item_name'], $_GET['item_type'], true + $_GET['item_name'], $_GET['item_type'], true, true ); if ($routine !== false) { $form = PMA_RTN_getExecuteForm($routine); diff --git a/libraries/rte/rte_words.lib.php b/libraries/rte/rte_words.lib.php index 18d757d25218..ff301874df87 100644 --- a/libraries/rte/rte_words.lib.php +++ b/libraries/rte/rte_words.lib.php @@ -31,6 +31,12 @@ function PMA_RTE_getWord($index) 'no_create' => __( 'You do not have the necessary privileges to create a routine' ), + 'no_edit' => __( + 'You do not have the necessary privileges to edit this routine' + ), + 'no_view' => __( + 'You do not have the necessary privileges to view/export this routine' + ), 'not_found' => __('No routine with name %1$s found in database %2$s'), 'nothing' => __('There are no routines to display.'), 'title' => __('Routines'), From 815bf44584f104d1616ca1d05a2aaba34580a418 Mon Sep 17 00:00:00 2001 From: Deven Bansod Date: Thu, 8 Sep 2016 10:39:19 +0530 Subject: [PATCH 2/2] Fix error messages to suit better localization Signed-off-by: Deven Bansod --- libraries/rte/rte_export.lib.php | 3 +-- libraries/rte/rte_routines.lib.php | 3 +-- libraries/rte/rte_words.lib.php | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/rte/rte_export.lib.php b/libraries/rte/rte_export.lib.php index 19fa6dd0fb15..94b59ff5aaa6 100644 --- a/libraries/rte/rte_export.lib.php +++ b/libraries/rte/rte_export.lib.php @@ -44,8 +44,7 @@ function PMA_RTE_handleExport($export_data) } else { $_db = htmlspecialchars(PMA\libraries\Util::backquote($db)); $message = __('Error in processing request:') . ' ' - . sprintf(PMA_RTE_getWord('not_found'), $item_name, $_db) - . ' OR ' . PMA_RTE_getWord('no_view'); + . sprintf(PMA_RTE_getWord('no_view'), $item_name, $_db); $message = Message::error($message); if ($GLOBALS['is_ajax_request'] == true) { diff --git a/libraries/rte/rte_routines.lib.php b/libraries/rte/rte_routines.lib.php index 21eb9bf9db88..2db6fcf79c87 100644 --- a/libraries/rte/rte_routines.lib.php +++ b/libraries/rte/rte_routines.lib.php @@ -155,13 +155,12 @@ function PMA_RTN_handleEditor() } else { $message = __('Error in processing request:') . ' '; $message .= sprintf( - PMA_RTE_getWord('not_found'), + PMA_RTE_getWord('no_edit'), htmlspecialchars( PMA\libraries\Util::backquote($_REQUEST['item_name']) ), htmlspecialchars(PMA\libraries\Util::backquote($db)) ); - $message .= ' OR ' . PMA_RTE_getWord('no_edit'); $message = Message::error($message); if ($GLOBALS['is_ajax_request']) { diff --git a/libraries/rte/rte_words.lib.php b/libraries/rte/rte_words.lib.php index ff301874df87..3205c2e7fb1e 100644 --- a/libraries/rte/rte_words.lib.php +++ b/libraries/rte/rte_words.lib.php @@ -32,10 +32,12 @@ function PMA_RTE_getWord($index) 'You do not have the necessary privileges to create a routine' ), 'no_edit' => __( - 'You do not have the necessary privileges to edit this routine' + 'No routine with name %1$s found in database %2$s. ' + . 'You might be lacking the necessary privileges to edit this routine' ), 'no_view' => __( - 'You do not have the necessary privileges to view/export this routine' + 'No routine with name %1$s found in database %2$s. ' + . 'You might be lacking the necessary privileges to view/export this routine' ), 'not_found' => __('No routine with name %1$s found in database %2$s'), 'nothing' => __('There are no routines to display.'),