[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/report/security/ -> locallib.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Lib functions
  19   *
  20   * @package    report
  21   * @subpackage security
  22   * @copyright  2008 petr Skoda
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die;
  27  
  28  
  29  define('REPORT_SECURITY_OK', 'ok');
  30  define('REPORT_SECURITY_INFO', 'info');
  31  define('REPORT_SECURITY_WARNING', 'warning');
  32  define('REPORT_SECURITY_SERIOUS', 'serious');
  33  define('REPORT_SECURITY_CRITICAL', 'critical');
  34  
  35  function report_security_hide_timearning() {
  36       global $PAGE;
  37       $PAGE->requires->js_init_code("Y.one('#timewarning').addClass('timewarninghidden')");
  38  }
  39  
  40  function report_security_get_issue_list() {
  41      return array(
  42          'report_security_check_unsecuredataroot',
  43          'report_security_check_displayerrors',
  44          'report_security_check_noauth',
  45          'report_security_check_embed',
  46          'report_security_check_mediafilterswf',
  47          'report_security_check_openprofiles',
  48          'report_security_check_google',
  49          'report_security_check_passwordpolicy',
  50          'report_security_check_emailchangeconfirmation',
  51          'report_security_check_cookiesecure',
  52          'report_security_check_configrw',
  53          'report_security_check_riskxss',
  54          'report_security_check_riskadmin',
  55          'report_security_check_riskbackup',
  56          'report_security_check_defaultuserrole',
  57          'report_security_check_guestrole',
  58          'report_security_check_frontpagerole',
  59          'report_security_check_webcron',
  60  
  61      );
  62  }
  63  
  64  function report_security_doc_link($issue, $name) {
  65      global $CFG, $OUTPUT;
  66  
  67      if (empty($CFG->docroot)) {
  68          return $name;
  69      }
  70  
  71      return $OUTPUT->doc_link('report/security/'.$issue, $name);
  72  }
  73  
  74  ///=============================================
  75  ///               Issue checks
  76  ///=============================================
  77  
  78  
  79  /**
  80   * Verifies unsupported noauth setting
  81   * @param bool $detailed
  82   * @return object result
  83   */
  84  function report_security_check_noauth($detailed=false) {
  85      global $CFG;
  86  
  87      $result = new stdClass();
  88      $result->issue   = 'report_security_check_noauth';
  89      $result->name    = get_string('check_noauth_name', 'report_security');
  90      $result->info    = null;
  91      $result->details = null;
  92      $result->status  = null;
  93      $result->link    = null;
  94      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=manageauths\">".get_string('authsettings', 'admin').'</a>';
  95  
  96      if (is_enabled_auth('none')) {
  97          $result->status = REPORT_SECURITY_CRITICAL;
  98          $result->info   = get_string('check_noauth_error', 'report_security');
  99      } else {
 100          $result->status = REPORT_SECURITY_OK;
 101          $result->info   = get_string('check_noauth_ok', 'report_security');
 102      }
 103  
 104      if ($detailed) {
 105          $result->details = get_string('check_noauth_details', 'report_security');
 106      }
 107  
 108      return $result;
 109  }
 110  
 111  /**
 112   * Verifies if password policy set
 113   * @param bool $detailed
 114   * @return object result
 115   */
 116  function report_security_check_passwordpolicy($detailed=false) {
 117      global $CFG;
 118  
 119      $result = new stdClass();
 120      $result->issue   = 'report_security_check_passwordpolicy';
 121      $result->name    = get_string('check_passwordpolicy_name', 'report_security');
 122      $result->info    = null;
 123      $result->details = null;
 124      $result->status  = null;
 125      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
 126  
 127      if (empty($CFG->passwordpolicy)) {
 128          $result->status = REPORT_SECURITY_WARNING;
 129          $result->info   = get_string('check_passwordpolicy_error', 'report_security');
 130      } else {
 131          $result->status = REPORT_SECURITY_OK;
 132          $result->info   = get_string('check_passwordpolicy_ok', 'report_security');
 133      }
 134  
 135      if ($detailed) {
 136          $result->details = get_string('check_passwordpolicy_details', 'report_security');
 137      }
 138  
 139      return $result;
 140  }
 141  
 142  /**
 143   * Verifies sloppy embedding - this should have been removed long ago!!
 144   * @param bool $detailed
 145   * @return object result
 146   */
 147  function report_security_check_embed($detailed=false) {
 148      global $CFG;
 149  
 150      $result = new stdClass();
 151      $result->issue   = 'report_security_check_embed';
 152      $result->name    = get_string('check_embed_name', 'report_security');
 153      $result->info    = null;
 154      $result->details = null;
 155      $result->status  = null;
 156      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
 157  
 158      if (!empty($CFG->allowobjectembed)) {
 159          $result->status = REPORT_SECURITY_CRITICAL;
 160          $result->info   = get_string('check_embed_error', 'report_security');
 161      } else {
 162          $result->status = REPORT_SECURITY_OK;
 163          $result->info   = get_string('check_embed_ok', 'report_security');
 164      }
 165  
 166      if ($detailed) {
 167          $result->details = get_string('check_embed_details', 'report_security');
 168      }
 169  
 170      return $result;
 171  }
 172  
 173  /**
 174   * Verifies sloppy swf embedding - this should have been removed long ago!!
 175   * @param bool $detailed
 176   * @return object result
 177   */
 178  function report_security_check_mediafilterswf($detailed=false) {
 179      global $CFG;
 180  
 181      $result = new stdClass();
 182      $result->issue   = 'report_security_check_mediafilterswf';
 183      $result->name    = get_string('check_mediafilterswf_name', 'report_security');
 184      $result->info    = null;
 185      $result->details = null;
 186      $result->status  = null;
 187      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=filtersettingfiltermediaplugin\">".get_string('filtersettings', 'admin').'</a>';
 188  
 189      $activefilters = filter_get_globally_enabled();
 190  
 191      if (array_search('mediaplugin', $activefilters) !== false and !empty($CFG->filter_mediaplugin_enable_swf)) {
 192          $result->status = REPORT_SECURITY_CRITICAL;
 193          $result->info   = get_string('check_mediafilterswf_error', 'report_security');
 194      } else {
 195          $result->status = REPORT_SECURITY_OK;
 196          $result->info   = get_string('check_mediafilterswf_ok', 'report_security');
 197      }
 198  
 199      if ($detailed) {
 200          $result->details = get_string('check_mediafilterswf_details', 'report_security');
 201      }
 202  
 203      return $result;
 204  }
 205  
 206  /**
 207   * Verifies fatal misconfiguration of dataroot
 208   * @param bool $detailed
 209   * @return object result
 210   */
 211  function report_security_check_unsecuredataroot($detailed=false) {
 212      global $CFG;
 213  
 214      $result = new stdClass();
 215      $result->issue   = 'report_security_check_unsecuredataroot';
 216      $result->name    = get_string('check_unsecuredataroot_name', 'report_security');
 217      $result->info    = null;
 218      $result->details = null;
 219      $result->status  = null;
 220      $result->link    = null;
 221  
 222      $insecuredataroot = is_dataroot_insecure(true);
 223  
 224      if ($insecuredataroot == INSECURE_DATAROOT_WARNING) {
 225          $result->status = REPORT_SECURITY_SERIOUS;
 226          $result->info   = get_string('check_unsecuredataroot_warning', 'report_security', $CFG->dataroot);
 227  
 228      } else if ($insecuredataroot == INSECURE_DATAROOT_ERROR) {
 229          $result->status = REPORT_SECURITY_CRITICAL;
 230          $result->info   = get_string('check_unsecuredataroot_error', 'report_security', $CFG->dataroot);
 231  
 232      } else {
 233          $result->status = REPORT_SECURITY_OK;
 234          $result->info   = get_string('check_unsecuredataroot_ok', 'report_security');
 235      }
 236  
 237      if ($detailed) {
 238          $result->details = get_string('check_unsecuredataroot_details', 'report_security');
 239      }
 240  
 241      return $result;
 242  }
 243  
 244  /**
 245   * Verifies displaying of errors - problem for lib files and 3rd party code
 246   * because we can not disable debugging in these scripts (they do not include config.php)
 247   * @param bool $detailed
 248   * @return object result
 249   */
 250  function report_security_check_displayerrors($detailed=false) {
 251      $result = new stdClass();
 252      $result->issue   = 'report_security_check_displayerrors';
 253      $result->name    = get_string('check_displayerrors_name', 'report_security');
 254      $result->info    = null;
 255      $result->details = null;
 256      $result->status  = null;
 257      $result->link    = null;
 258  
 259      if (defined('WARN_DISPLAY_ERRORS_ENABLED')) {
 260          $result->status = REPORT_SECURITY_WARNING;
 261          $result->info   = get_string('check_displayerrors_error', 'report_security');
 262      } else {
 263          $result->status = REPORT_SECURITY_OK;
 264          $result->info   = get_string('check_displayerrors_ok', 'report_security');
 265      }
 266  
 267      if ($detailed) {
 268          $result->details = get_string('check_displayerrors_details', 'report_security');
 269      }
 270  
 271      return $result;
 272  }
 273  
 274  /**
 275   * Verifies open profiles - originally open by default, not anymore because spammer abused it a lot
 276   * @param bool $detailed
 277   * @return object result
 278   */
 279  function report_security_check_openprofiles($detailed=false) {
 280      global $CFG;
 281  
 282      $result = new stdClass();
 283      $result->issue   = 'report_security_check_openprofiles';
 284      $result->name    = get_string('check_openprofiles_name', 'report_security');
 285      $result->info    = null;
 286      $result->details = null;
 287      $result->status  = null;
 288      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
 289  
 290      if (empty($CFG->forcelogin) and empty($CFG->forceloginforprofiles)) {
 291          $result->status = REPORT_SECURITY_WARNING;
 292          $result->info   = get_string('check_openprofiles_error', 'report_security');
 293      } else {
 294          $result->status = REPORT_SECURITY_OK;
 295          $result->info   = get_string('check_openprofiles_ok', 'report_security');
 296      }
 297  
 298      if ($detailed) {
 299          $result->details = get_string('check_openprofiles_details', 'report_security');
 300      }
 301  
 302      return $result;
 303  }
 304  
 305  /**
 306   * Verifies google access not combined with disabled guest access
 307   * because attackers might gain guest access by modifying browser signature.
 308   * @param bool $detailed
 309   * @return object result
 310   */
 311  function report_security_check_google($detailed=false) {
 312      global $CFG;
 313  
 314      $result = new stdClass();
 315      $result->issue   = 'report_security_check_google';
 316      $result->name    = get_string('check_google_name', 'report_security');
 317      $result->info    = null;
 318      $result->details = null;
 319      $result->status  = null;
 320      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
 321  
 322      if (empty($CFG->opentogoogle)) {
 323          $result->status = REPORT_SECURITY_OK;
 324          $result->info   = get_string('check_google_ok', 'report_security');
 325      } else if (!empty($CFG->guestloginbutton)) {
 326          $result->status = REPORT_SECURITY_INFO;
 327          $result->info   = get_string('check_google_info', 'report_security');
 328      } else {
 329          $result->status = REPORT_SECURITY_SERIOUS;
 330          $result->info   = get_string('check_google_error', 'report_security');
 331      }
 332  
 333      if ($detailed) {
 334          $result->details = get_string('check_google_details', 'report_security');
 335      }
 336  
 337      return $result;
 338  }
 339  
 340  /**
 341   * Verifies email confirmation - spammers were changing mails very often
 342   * @param bool $detailed
 343   * @return object result
 344   */
 345  function report_security_check_emailchangeconfirmation($detailed=false) {
 346      global $CFG;
 347  
 348      $result = new stdClass();
 349      $result->issue   = 'report_security_check_emailchangeconfirmation';
 350      $result->name    = get_string('check_emailchangeconfirmation_name', 'report_security');
 351      $result->info    = null;
 352      $result->details = null;
 353      $result->status  = null;
 354      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">".get_string('sitepolicies', 'admin').'</a>';
 355  
 356      if (empty($CFG->emailchangeconfirmation)) {
 357          if (empty($CFG->allowemailaddresses)) {
 358              $result->status = REPORT_SECURITY_WARNING;
 359              $result->info   = get_string('check_emailchangeconfirmation_error', 'report_security');
 360          } else {
 361              $result->status = REPORT_SECURITY_INFO;
 362              $result->info   = get_string('check_emailchangeconfirmation_info', 'report_security');
 363          }
 364      } else {
 365          $result->status = REPORT_SECURITY_OK;
 366          $result->info   = get_string('check_emailchangeconfirmation_ok', 'report_security');
 367      }
 368  
 369      if ($detailed) {
 370          $result->details = get_string('check_emailchangeconfirmation_details', 'report_security');
 371      }
 372  
 373      return $result;
 374  }
 375  
 376  /**
 377   * Verifies if https enabled only secure cookies allowed,
 378   * this prevents redirections and sending of cookies to unsecure port.
 379   * @param bool $detailed
 380   * @return object result
 381   */
 382  function report_security_check_cookiesecure($detailed=false) {
 383      global $CFG;
 384  
 385      if (!is_https()) {
 386          return null;
 387      }
 388  
 389      $result = new stdClass();
 390      $result->issue   = 'report_security_check_cookiesecure';
 391      $result->name    = get_string('check_cookiesecure_name', 'report_security');
 392      $result->info    = null;
 393      $result->details = null;
 394      $result->status  = null;
 395      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=httpsecurity\">".get_string('httpsecurity', 'admin').'</a>';
 396  
 397      if (empty($CFG->cookiesecure)) {
 398          $result->status = REPORT_SECURITY_SERIOUS;
 399          $result->info   = get_string('check_cookiesecure_error', 'report_security');
 400      } else {
 401          $result->status = REPORT_SECURITY_OK;
 402          $result->info   = get_string('check_cookiesecure_ok', 'report_security');
 403      }
 404  
 405      if ($detailed) {
 406          $result->details = get_string('check_cookiesecure_details', 'report_security');
 407      }
 408  
 409      return $result;
 410  }
 411  
 412  /**
 413   * Verifies config.php is not writable anymore after installation,
 414   * config files were changed on several outdated server.
 415   * @param bool $detailed
 416   * @return object result
 417   */
 418  function report_security_check_configrw($detailed=false) {
 419      global $CFG;
 420  
 421      $result = new stdClass();
 422      $result->issue   = 'report_security_check_configrw';
 423      $result->name    = get_string('check_configrw_name', 'report_security');
 424      $result->info    = null;
 425      $result->details = null;
 426      $result->status  = null;
 427      $result->link    = null;
 428  
 429      if (is_writable($CFG->dirroot.'/config.php')) {
 430          $result->status = REPORT_SECURITY_WARNING;
 431          $result->info   = get_string('check_configrw_warning', 'report_security');
 432      } else {
 433          $result->status = REPORT_SECURITY_OK;
 434          $result->info   = get_string('check_configrw_ok', 'report_security');
 435      }
 436  
 437      if ($detailed) {
 438          $result->details = get_string('check_configrw_details', 'report_security');
 439      }
 440  
 441      return $result;
 442  }
 443  
 444  
 445  /**
 446   * Lists all users with XSS risk, it would be great to combine this with risk trusts in user table,
 447   * unfortunately nobody implemented user trust UI yet :-(
 448   * @param bool $detailed
 449   * @return object result
 450   */
 451  function report_security_check_riskxss($detailed=false) {
 452      global $DB;
 453  
 454      $result = new stdClass();
 455      $result->issue   = 'report_security_check_riskxss';
 456      $result->name    = get_string('check_riskxss_name', 'report_security');
 457      $result->info    = null;
 458      $result->details = null;
 459      $result->status  = REPORT_SECURITY_WARNING;
 460      $result->link    = null;
 461  
 462      $params = array('capallow'=>CAP_ALLOW);
 463  
 464      $sqlfrom = "FROM (SELECT rcx.*
 465                         FROM {role_capabilities} rcx
 466                         JOIN {capabilities} cap ON (cap.name = rcx.capability AND ".$DB->sql_bitand('cap.riskbitmask', RISK_XSS)." <> 0)
 467                         WHERE rcx.permission = :capallow) rc,
 468                       {context} c,
 469                       {context} sc,
 470                       {role_assignments} ra,
 471                       {user} u
 472                 WHERE c.id = rc.contextid
 473                       AND (sc.path = c.path OR sc.path LIKE ".$DB->sql_concat('c.path', "'/%'")." OR c.path LIKE ".$DB->sql_concat('sc.path', "'/%'").")
 474                       AND u.id = ra.userid AND u.deleted = 0
 475                       AND ra.contextid = sc.id AND ra.roleid = rc.roleid";
 476  
 477      $count = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) $sqlfrom", $params);
 478  
 479      $result->info = get_string('check_riskxss_warning', 'report_security', $count);
 480  
 481      if ($detailed) {
 482          $userfields = user_picture::fields('u');
 483          $users = $DB->get_records_sql("SELECT DISTINCT $userfields $sqlfrom", $params);
 484          foreach ($users as $uid=>$user) {
 485              $users[$uid] = fullname($user);
 486          }
 487          $users = implode(', ', $users);
 488          $result->details = get_string('check_riskxss_details', 'report_security', $users);
 489      }
 490  
 491      return $result;
 492  }
 493  
 494  /**
 495   * Verifies sanity of default user role.
 496   * @param bool $detailed
 497   * @return object result
 498   */
 499  function report_security_check_defaultuserrole($detailed=false) {
 500      global $DB, $CFG;
 501  
 502      $result = new stdClass();
 503      $result->issue   = 'report_security_check_defaultuserrole';
 504      $result->name    = get_string('check_defaultuserrole_name', 'report_security');
 505      $result->info    = null;
 506      $result->details = null;
 507      $result->status  = null;
 508      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'</a>';
 509  
 510      if (!$default_role = $DB->get_record('role', array('id'=>$CFG->defaultuserroleid))) {
 511          $result->status  = REPORT_SECURITY_WARNING;
 512          $result->info    = get_string('check_defaultuserrole_notset', 'report_security');
 513          $result->details = $result->info;
 514  
 515          return $result;
 516      }
 517  
 518      // risky caps - usually very dangerous
 519      $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$default_role->id);
 520      $sql = "SELECT COUNT(DISTINCT rc.contextid)
 521                FROM {role_capabilities} rc
 522                JOIN {capabilities} cap ON cap.name = rc.capability
 523               WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
 524                     AND rc.permission = :capallow
 525                     AND rc.roleid = :roleid";
 526  
 527      $riskycount = $DB->count_records_sql($sql, $params);
 528  
 529      // it may have either none or 'user' archetype - nothing else, or else it would break during upgrades badly
 530      if ($default_role->archetype === '' or $default_role->archetype === 'user') {
 531          $legacyok = true;
 532      } else {
 533          $legacyok = false;
 534      }
 535  
 536      if ($riskycount or !$legacyok) {
 537          $result->status  = REPORT_SECURITY_CRITICAL;
 538          $result->info    = get_string('check_defaultuserrole_error', 'report_security', role_get_name($default_role));
 539  
 540      } else {
 541          $result->status  = REPORT_SECURITY_OK;
 542          $result->info    = get_string('check_defaultuserrole_ok', 'report_security');
 543      }
 544  
 545      if ($detailed) {
 546          $result->details = get_string('check_defaultuserrole_details', 'report_security');
 547      }
 548  
 549      return $result;
 550  }
 551  
 552  /**
 553   * Verifies sanity of guest role
 554   * @param bool $detailed
 555   * @return object result
 556   */
 557  function report_security_check_guestrole($detailed=false) {
 558      global $DB, $CFG;
 559  
 560      $result = new stdClass();
 561      $result->issue   = 'report_security_check_guestrole';
 562      $result->name    = get_string('check_guestrole_name', 'report_security');
 563      $result->info    = null;
 564      $result->details = null;
 565      $result->status  = null;
 566      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'</a>';
 567  
 568      if (!$guest_role = $DB->get_record('role', array('id'=>$CFG->guestroleid))) {
 569          $result->status  = REPORT_SECURITY_WARNING;
 570          $result->info    = get_string('check_guestrole_notset', 'report_security');
 571          $result->details = $result->info;
 572  
 573          return $result;
 574      }
 575  
 576      // risky caps - usually very dangerous
 577      $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$guest_role->id);
 578      $sql = "SELECT COUNT(DISTINCT rc.contextid)
 579                FROM {role_capabilities} rc
 580                JOIN {capabilities} cap ON cap.name = rc.capability
 581               WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
 582                     AND rc.permission = :capallow
 583                     AND rc.roleid = :roleid";
 584  
 585      $riskycount = $DB->count_records_sql($sql, $params);
 586  
 587      // it may have either no or 'guest' archetype - nothing else, or else it would break during upgrades badly
 588      if ($guest_role->archetype === '' or $guest_role->archetype === 'guest') {
 589          $legacyok = true;
 590      } else {
 591          $legacyok = false;
 592      }
 593  
 594      if ($riskycount or !$legacyok) {
 595          $result->status  = REPORT_SECURITY_CRITICAL;
 596          $result->info    = get_string('check_guestrole_error', 'report_security', format_string($guest_role->name));
 597  
 598      } else {
 599          $result->status  = REPORT_SECURITY_OK;
 600          $result->info    = get_string('check_guestrole_ok', 'report_security');
 601      }
 602  
 603      if ($detailed) {
 604          $result->details = get_string('check_guestrole_details', 'report_security');
 605      }
 606  
 607      return $result;
 608  }
 609  
 610  /**
 611   * Verifies sanity of frontpage role
 612   * @param bool $detailed
 613   * @return object result
 614   */
 615  function report_security_check_frontpagerole($detailed=false) {
 616      global $DB, $CFG;
 617  
 618      $result = new stdClass();
 619      $result->issue   = 'report_security_check_frontpagerole';
 620      $result->name    = get_string('check_frontpagerole_name', 'report_security');
 621      $result->info    = null;
 622      $result->details = null;
 623      $result->status  = null;
 624      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=frontpagesettings\">".get_string('frontpagesettings','admin').'</a>';
 625  
 626      if (!$frontpage_role = $DB->get_record('role', array('id'=>$CFG->defaultfrontpageroleid))) {
 627          $result->status  = REPORT_SECURITY_INFO;
 628          $result->info    = get_string('check_frontpagerole_notset', 'report_security');
 629          $result->details = get_string('check_frontpagerole_details', 'report_security');
 630  
 631          return $result;
 632      }
 633  
 634      // risky caps - usually very dangerous
 635      $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$frontpage_role->id);
 636      $sql = "SELECT COUNT(DISTINCT rc.contextid)
 637                FROM {role_capabilities} rc
 638                JOIN {capabilities} cap ON cap.name = rc.capability
 639               WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
 640                     AND rc.permission = :capallow
 641                     AND rc.roleid = :roleid";
 642  
 643      $riskycount = $DB->count_records_sql($sql, $params);
 644  
 645      // there is no legacy role type for frontpage yet - anyway we can not allow teachers or admins there!
 646      if ($frontpage_role->archetype === 'teacher' or $frontpage_role->archetype === 'editingteacher'
 647        or $frontpage_role->archetype === 'coursecreator' or $frontpage_role->archetype === 'manager') {
 648          $legacyok = false;
 649      } else {
 650          $legacyok = true;
 651      }
 652  
 653      if ($riskycount or !$legacyok) {
 654          $result->status  = REPORT_SECURITY_CRITICAL;
 655          $result->info    = get_string('check_frontpagerole_error', 'report_security', format_string($frontpage_role->name));
 656  
 657      } else {
 658          $result->status  = REPORT_SECURITY_OK;
 659          $result->info    = get_string('check_frontpagerole_ok', 'report_security');
 660      }
 661  
 662      if ($detailed) {
 663          $result->details = get_string('check_frontpagerole_details', 'report_security');
 664      }
 665  
 666      return $result;
 667  }
 668  
 669  /**
 670   * Lists all admins.
 671   * @param bool $detailed
 672   * @return object result
 673   */
 674  function report_security_check_riskadmin($detailed=false) {
 675      global $DB, $CFG;
 676  
 677      $result = new stdClass();
 678      $result->issue   = 'report_security_check_riskadmin';
 679      $result->name    = get_string('check_riskadmin_name', 'report_security');
 680      $result->info    = null;
 681      $result->details = null;
 682      $result->status  = null;
 683      $result->link    = null;
 684  
 685      $userfields = user_picture::fields('u');
 686      $sql = "SELECT $userfields
 687                FROM {user} u
 688               WHERE u.id IN ($CFG->siteadmins)";
 689  
 690      $admins = $DB->get_records_sql($sql);
 691      $admincount = count($admins);
 692  
 693      if ($detailed) {
 694          foreach ($admins as $uid=>$user) {
 695              $url = "$CFG->wwwroot/user/view.php?id=$user->id";
 696              $admins[$uid] = '<li><a href="'.$url.'">'.fullname($user).' ('.$user->email.')</a></li>';
 697          }
 698          $admins = '<ul>'.implode('', $admins).'</ul>';
 699      }
 700  
 701      $result->status  = REPORT_SECURITY_OK;
 702      $result->info = get_string('check_riskadmin_ok', 'report_security', $admincount);
 703  
 704      if ($detailed) {
 705          $result->details = get_string('check_riskadmin_detailsok', 'report_security', $admins);
 706      }
 707  
 708      return $result;
 709  }
 710  
 711  /**
 712   * Lists all roles that have the ability to backup user data, as well as users
 713   * @param bool $detailed
 714   * @return object result
 715   */
 716  function report_security_check_riskbackup($detailed=false) {
 717      global $CFG, $DB;
 718  
 719      $result = new stdClass();
 720      $result->issue   = 'report_security_check_riskbackup';
 721      $result->name    = get_string('check_riskbackup_name', 'report_security');
 722      $result->info    = null;
 723      $result->details = null;
 724      $result->status  = null;
 725      $result->link    = null;
 726  
 727      $syscontext = context_system::instance();
 728  
 729      $params = array('capability'=>'moodle/backup:userinfo', 'permission'=>CAP_ALLOW, 'contextid'=>$syscontext->id);
 730      $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder, r.archetype
 731                FROM {role} r
 732                JOIN {role_capabilities} rc ON rc.roleid = r.id
 733               WHERE rc.capability = :capability
 734                 AND rc.contextid  = :contextid
 735                 AND rc.permission = :permission";
 736      $systemroles = $DB->get_records_sql($sql, $params);
 737  
 738      $params = array('capability'=>'moodle/backup:userinfo', 'permission'=>CAP_ALLOW, 'contextid'=>$syscontext->id);
 739      $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder, r.archetype, rc.contextid
 740                FROM {role} r
 741                JOIN {role_capabilities} rc ON rc.roleid = r.id
 742               WHERE rc.capability = :capability
 743                 AND rc.contextid <> :contextid
 744                 AND rc.permission = :permission";
 745      $overriddenroles = $DB->get_records_sql($sql, $params);
 746  
 747      // list of users that are able to backup personal info
 748      // note: "sc" is context where is role assigned,
 749      //       "c" is context where is role overridden or system context if in role definition
 750      $params = array('capability'=>'moodle/backup:userinfo', 'permission'=>CAP_ALLOW, 'context1'=>CONTEXT_COURSE, 'context2'=>CONTEXT_COURSE);
 751  
 752      $sqluserinfo = "
 753          FROM (SELECT rcx.*
 754                  FROM {role_capabilities} rcx
 755                 WHERE rcx.permission = :permission AND rcx.capability = :capability) rc,
 756               {context} c,
 757               {context} sc,
 758               {role_assignments} ra,
 759               {user} u
 760         WHERE c.id = rc.contextid
 761               AND (sc.path = c.path OR sc.path LIKE ".$DB->sql_concat('c.path', "'/%'")." OR c.path LIKE ".$DB->sql_concat('sc.path', "'/%'").")
 762               AND u.id = ra.userid AND u.deleted = 0
 763               AND ra.contextid = sc.id AND ra.roleid = rc.roleid
 764               AND sc.contextlevel <= :context1 AND c.contextlevel <= :context2";
 765  
 766      $usercount = $DB->count_records_sql("SELECT COUNT('x') FROM (SELECT DISTINCT u.id $sqluserinfo) userinfo", $params);
 767      $systemrolecount = empty($systemroles) ? 0 : count($systemroles);
 768      $overriddenrolecount = empty($overriddenroles) ? 0 : count($overriddenroles);
 769  
 770      if (max($usercount, $systemrolecount, $overriddenrolecount) > 0) {
 771          $result->status = REPORT_SECURITY_WARNING;
 772      } else {
 773          $result->status = REPORT_SECURITY_OK;
 774      }
 775  
 776      $a = (object)array('rolecount'=>$systemrolecount,'overridecount'=>$overriddenrolecount,'usercount'=>$usercount);
 777      $result->info = get_string('check_riskbackup_warning', 'report_security', $a);
 778  
 779      if ($detailed) {
 780  
 781          $result->details = '';  // Will be added to later
 782  
 783          // Make a list of roles
 784          if ($systemroles) {
 785              $links = array();
 786              foreach ($systemroles as $role) {
 787                  $role->name = role_get_name($role);
 788                  $role->url = "$CFG->wwwroot/$CFG->admin/roles/manage.php?action=edit&amp;roleid=$role->id";
 789                  $links[] = '<li>'.get_string('check_riskbackup_editrole', 'report_security', $role).'</li>';
 790              }
 791              $links = '<ul>'.implode($links).'</ul>';
 792              $result->details .= get_string('check_riskbackup_details_systemroles', 'report_security', $links);
 793          }
 794  
 795          // Make a list of overrides to roles
 796          $rolelinks2 = array();
 797          if ($overriddenroles) {
 798              $links = array();
 799              foreach ($overriddenroles as $role) {
 800                  $role->name = $role->localname;
 801                  $context = context::instance_by_id($role->contextid);
 802                  $role->name = role_get_name($role, $context, ROLENAME_BOTH);
 803                  $role->contextname = $context->get_context_name();
 804                  $role->url = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$role->contextid&amp;roleid=$role->id";
 805                  $links[] = '<li>'.get_string('check_riskbackup_editoverride', 'report_security', $role).'</li>';
 806              }
 807              $links = '<ul>'.implode('', $links).'</ul>';
 808              $result->details .= get_string('check_riskbackup_details_overriddenroles', 'report_security', $links);
 809          }
 810  
 811          // Get a list of affected users as well
 812          $users = array();
 813  
 814          list($sort, $sortparams) = users_order_by_sql('u');
 815          $userfields = user_picture::fields('u');
 816          $rs = $DB->get_recordset_sql("SELECT DISTINCT $userfields, ra.contextid, ra.roleid
 817              $sqluserinfo ORDER BY $sort", array_merge($params, $sortparams));
 818  
 819          foreach ($rs as $user) {
 820              $context = context::instance_by_id($user->contextid);
 821              $url = "$CFG->wwwroot/$CFG->admin/roles/assign.php?contextid=$user->contextid&amp;roleid=$user->roleid";
 822              $a = (object)array('fullname'=>fullname($user), 'url'=>$url, 'email'=>$user->email,
 823                                 'contextname'=>$context->get_context_name());
 824              $users[] = '<li>'.get_string('check_riskbackup_unassign', 'report_security', $a).'</li>';
 825          }
 826          if (!empty($users)) {
 827              $users = '<ul>'.implode('', $users).'</ul>';
 828              $result->details .= get_string('check_riskbackup_details_users', 'report_security', $users);
 829          }
 830      }
 831  
 832      return $result;
 833  }
 834  
 835  /**
 836   * Verifies the status of web cron
 837   *
 838   * @param bool $detailed
 839   * @return object result
 840   */
 841  function report_security_check_webcron($detailed = false) {
 842      global $CFG;
 843  
 844      $croncli = $CFG->cronclionly;
 845      $cronremotepassword = $CFG->cronremotepassword;
 846  
 847      $result = new stdClass();
 848      $result->issue   = 'report_security_check_webcron';
 849      $result->name    = get_string('check_webcron_name', 'report_security');
 850      $result->details = null;
 851      $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=sitepolicies\">"
 852              .get_string('sitepolicies', 'admin').'</a>';
 853  
 854      if (empty($croncli) && empty($cronremotepassword)) {
 855          $result->status = REPORT_SECURITY_WARNING;
 856          $result->info   = get_string('check_webcron_warning', 'report_security');
 857      } else {
 858          $result->status = REPORT_SECURITY_OK;
 859          $result->info   = get_string('check_webcron_ok', 'report_security');
 860      }
 861  
 862      if ($detailed) {
 863          $result->details = get_string('check_webcron_details', 'report_security');
 864      }
 865  
 866      return $result;
 867  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1