From becde58952e7c9d1bf0a66de84c166fbfac8e7b4 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Mon, 5 Dec 2016 15:10:32 +0100 Subject: [PATCH] Add sudo mode to enabling and disabling apps Otherwise an administrator could bypass sudo mode by installing an app that allows RCE by design. I've by intention excluded the update endpoint from the requirement because updating apps should be as unintruisive as possible. Not the cleanest approach by adding this to the AJAX endpoints instead of requiring a controller but for 11 this felt safer for me. We can clean this up together later then. (also the other AJAX endpoints in this folder do have the same logic) Ref https://github.com/nextcloud/server/issues/2487 Signed-off-by: Lukas Reschke --- settings/ajax/disableapp.php | 7 +++++++ settings/ajax/enableapp.php | 7 +++++++ settings/ajax/installapp.php | 7 +++++++ settings/ajax/uninstallapp.php | 7 +++++++ settings/js/apps.js | 10 ++++++++++ 5 files changed, 38 insertions(+) diff --git a/settings/ajax/disableapp.php b/settings/ajax/disableapp.php index 1a000672e6..8edd1c1453 100644 --- a/settings/ajax/disableapp.php +++ b/settings/ajax/disableapp.php @@ -24,6 +24,13 @@ OCP\JSON::checkAdminUser(); OCP\JSON::callCheck(); +$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); +if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay + $l = \OC::$server->getL10N('core'); + OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); + exit(); +} + if (!array_key_exists('appid', $_POST)) { OC_JSON::error(); exit; diff --git a/settings/ajax/enableapp.php b/settings/ajax/enableapp.php index cf1b7f29db..b6d62671a6 100644 --- a/settings/ajax/enableapp.php +++ b/settings/ajax/enableapp.php @@ -28,6 +28,13 @@ OC_JSON::checkAdminUser(); OCP\JSON::callCheck(); +$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); +if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay + $l = \OC::$server->getL10N('core'); + OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); + exit(); +} + $groups = isset($_POST['groups']) ? (array)$_POST['groups'] : null; try { diff --git a/settings/ajax/installapp.php b/settings/ajax/installapp.php index 75f3fea83b..17e5eadf50 100644 --- a/settings/ajax/installapp.php +++ b/settings/ajax/installapp.php @@ -24,6 +24,13 @@ OCP\JSON::checkAdminUser(); OCP\JSON::callCheck(); +$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); +if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay + $l = \OC::$server->getL10N('core'); + OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); + exit(); +} + if (!array_key_exists('appid', $_POST)) { OC_JSON::error(); exit; diff --git a/settings/ajax/uninstallapp.php b/settings/ajax/uninstallapp.php index be8196f4b3..0e68a893ef 100644 --- a/settings/ajax/uninstallapp.php +++ b/settings/ajax/uninstallapp.php @@ -24,6 +24,13 @@ OCP\JSON::checkAdminUser(); OCP\JSON::callCheck(); +$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); +if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay + $l = \OC::$server->getL10N('core'); + OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); + exit(); +} + if (!array_key_exists('appid', $_POST)) { OC_JSON::error(); exit; diff --git a/settings/js/apps.js b/settings/js/apps.js index 451becc67a..7c911427fa 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -269,6 +269,11 @@ OC.Settings.Apps = OC.Settings.Apps || { }, enableApp:function(appId, active, element, groups) { + if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { + OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.enableApp, this, appId, active, element, groups)); + return; + } + var self = this; OC.Settings.Apps.hideErrorMessage(appId); groups = groups || []; @@ -395,6 +400,11 @@ OC.Settings.Apps = OC.Settings.Apps || { }, uninstallApp:function(appId, element) { + if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { + OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.uninstallApp, this, appId, element)); + return; + } + OC.Settings.Apps.hideErrorMessage(appId); element.val(t('settings','Uninstalling ....')); $.post(OC.filePath('settings','ajax','uninstallapp.php'),{appid:appId},function(result) {