How does the function 'is_dataroot_insecure()' work?

How does the function 'is_dataroot_insecure()' work?

by Visvanath Ratnaweera -
Number of replies: 1
Picture of Particularly helpful Moodlers Picture of Translators
Moodle 2.7.11+ (Build: 20151110)

I have a case where I run in to "Insecure dataroot Serious Your dataroot directory /var/www/moodledata is in the wrong location and might be exposed to the web" in > Site administration > Reports > Security overview. (Details are in the discussion "Insecure dataroot (yet again)" https://moodle.org/mod/forum/discuss.php?d=323171". I can summarize it here, if it helps.)

My question is how exactly the function is_dataroot_insecure diagnoses the problem. Here is the code:
===
549    $testfile = $CFG->dataroot.'/diag/public.txt';
550    if (!file_exists($testfile)) {
551        file_put_contents($testfile, 'test file, do not delete');
552        @chmod($testfile, $CFG->filepermissions);
553    }
554    $teststr = trim(file_get_contents($testfile));
555    if (empty($teststr)) {
556    // hmm, strange
557        return INSECURE_DATAROOT_WARNING;
558    }
559
560    $testurl = $datarooturl.'/diag/public.txt';
561    if (extension_loaded('curl') and
562        !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
563        !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
564        ($ch = @curl_init($testurl)) !== false) {
565        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
566        curl_setopt($ch, CURLOPT_HEADER, false);
567        $data = curl_exec($ch);
568        if (!curl_errno($ch)) {
569            $data = trim($data);
570            if ($data === $teststr) {
571                curl_close($ch);
572                return INSECURE_DATAROOT_ERROR;
573            }
574        }
575        curl_close($ch);
576    }
577
578    if ($data = @file_get_contents($testurl)) {
579        $data = trim($data);
580        if ($data === $teststr) {
581            return INSECURE_DATAROOT_ERROR;
582        }
583    }
584
585    preg_match('|https?://([^/]+)|i', $testurl, $matches);
586    $sitename = $matches[1];
587    $error = 0;
588    if ($fp = @fsockopen($sitename, 80, $error)) {
589        preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
590        $localurl = $matches[1];
591        $out = "GET $localurl HTTP/1.1\r\n";
592        $out .= "Host: $sitename\r\n";
593        $out .= "Connection: Close\r\n\r\n";
594        fwrite($fp, $out);
595        $data = '';
596        $incoming = false;
597        while (!feof($fp)) {
598            if ($incoming) {
599                $data .= fgets($fp, 1024);
600            } else if (@fgets($fp, 1024) === "\r\n") {
601                    $incoming = true;
602            }
603        }
604        fclose($fp);
605        $data = trim($data);
606        if ($data === $teststr) {
607            return INSECURE_DATAROOT_ERROR;
608        }
609    }
610
611    return INSECURE_DATAROOT_WARNING;
612 }
===

That functions creates a directory $moodledata/diag and a file $moodledata/diag/public.txt with known content, if they don't already exist - through PHP directly, not through a network socket.

Q1. Shouldn't it try to delete them first? Those files could have been created during an earlier try. It is possible that the administrator has changed something, file permissions for example, and trying again. But will fail due to the previous attempt.

Q2. It would be very serious if moodledata is _writable_ through the network. Shouldn't that be detected and ring an alarm?

Then from line 588 it tries to read that file through a connection "GET /moodledata//diag/public.txt HTTP/1.1 Host: example.com Connection: Close".

It reads the response in the while loop and does not enter the line 607, as expected. Still the function exists with a INSECURE_DATAROOT_WARNING due to line 611. That is the "serious" warning I am getting.

Q3. Isn't it a bug?

Observation 1:  The $data in line 599 contains "Found The document has moved here (https://example.com/moodledata//diag/public.txt).

Observation 2: https://example.com/moodledata//diag/public.txt in the browser brings 404 Not found.
Average of ratings: -