Upload.module hard-codes 'view uploaded files' permission check

drewish - April 15, 2008 - 21:09
Project:Drupal
Version:6.x-dev
Component:upload.module
Category:bug report
Priority:critical
Assigned:Unassigned
Status:closed
Description

The upload module implements hook_file_download() in such a way that once it's enabled for any module to allow download the users must be granted 'view uploaded files' permission. The problem is that the module checks for permissions before checking that it's a file that it controls:

<?php
function upload_file_download($file) {
  if (!
user_access('view uploaded files')) {
    return -
1;
  }
 
$file = file_create_path($file);
 
$result = db_query("SELECT f.* FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = '%s'", $file);
  if (
$file = db_fetch_object($result)) {
    return array(
     
'Content-Type: ' . $file->filemime,
     
'Content-Length: ' . $file->filesize,
    );
  }
}
?>

The attached patch moves the permissions check down so that it's only enforced once the file is known to be associated with an {upload} record.

To test this you'd need to:
- enable private file transfers
- grant one role 'view uploaded files' permissions
- upload files to a node
- determine the download URL of the uploaded file
- log in as the user with the 'view uploaded files' permissions and ensure that they're able to view the file
- log in as the user without the 'view uploaded files' permissions and ensure that they aren't able to view the file
-

AttachmentSizeStatusTest resultOperations
upload_perms.patch1.1 KBIdleFailed: Failed to apply patch.View details

#1

drewish - April 15, 2008 - 21:12
Status:active» needs review

#2

quicksketch - April 15, 2008 - 21:59

Big +1 here.

Upload module shouldn't be preventing the downloading of any file it doesn't control. As it is, upload is preventing access to all files (even not in the Edit: files upload table).

#3

drewish - April 22, 2008 - 21:06

Over on #247780: No links on nodes I got a report from a user that this fixed their problem.

#4

Hetta - April 23, 2008 - 09:54

Is this for 7.x-dev only, or would it work on 6.x or 5.x, too?
Thanks!

#5

drewish - April 23, 2008 - 20:12

it will work on D6 as well, hopefully after we get it committed to D7 it can be backported to D6 as a bug fix.

#6

Nda - April 29, 2008 - 15:24

Hi, this fixed the problem for me. I've noticed other iffyness with the upload permissions on another module some time ago but can't remember which one. Thanks drewish for pointing me to this.

#7

quicksketch - April 29, 2008 - 19:14
Status:needs review» reviewed & tested by the community

Tested out and works great. The code change move is minor but critical for proper handling of files not uploaded through upload.module. This should be backported to 6.x. It probably won't be ported to 5.x though, because upload.module thinks it owns the files table in 5.x (in 6.x, upload.module was given it's own "upload" table).

#8

webchick - April 29, 2008 - 19:48

#9

flobruit - April 29, 2008 - 21:24
Status:reviewed & tested by the community» needs review

This is definitely a bug. The documentation for hook_file_download is suffering from the same problem, and does not indicate that the hook should not return anything (or NULL) for files not controlled by the current module, so here's a patch to fix that (applies to the docs in contrib).

Also, $file is used both as a string and as an object inside of upload_file_download(), and although PHP allows it, it's bad practice to use the same variable for many purposes. Since we're already patching upload_file_download(), we might as well fix it.

AttachmentSizeStatusTest resultOperations
upload_file_download-247095-7.patch1.01 KBIdleFailed: Failed to apply patch.View details
hook_file_download.patch1.45 KBIdleFailed: Failed to apply patch.View details

#10

drewish - May 13, 2008 - 20:44
Status:needs review» reviewed & tested by the community

flobruit I made some changes to your docs patch and went ahead and committed. I think you're also correct to use a different variable for the filepath.

#11

Dries - May 14, 2008 - 13:20
Version:7.x-dev» 6.x-dev

I've committed this to CVS HEAD. I'll leave it up to Gabor to commit to DRUPAL-6. I think it is safe to do, but some more eyeballs wouldn't hurt.

#12

Gábor Hojtsy - May 19, 2008 - 08:15
Version:6.x-dev» 7.x-dev
Status:reviewed & tested by the community» needs work

Looks like Dries was quick to commit this to Drupal 7. The function argument was renamed from $file to $filepath, which is a great thing, since $file is an object later in the same function. But the db_query() argument is $file which does not have any value at that time (it should also be $filepath as far as I see), or otherwise this will never work. Lets fix in D7 and then backport the right fix.

#13

drewish - May 26, 2008 - 23:21
Status:needs work» needs review

-1 for my patch reviewing karma. here's the fix.

AttachmentSizeStatusTest resultOperations
upload_247095.patch1 KBIdleFailed: Failed to apply patch.View details

#14

flobruit - June 2, 2008 - 22:07
Status:needs review» reviewed & tested by the community

-1 for my variable renaming skills. The fix is good.

#15

moshe weitzman - July 15, 2008 - 12:35

This is a simple bug fix. Lets commit.

#16

Damien Tournoud - July 17, 2008 - 06:40
Title:upload.module hard-codes 'view uploaded files' permission check» Tests needed: upload.module hard-codes 'view uploaded files' permission check
Priority:normal» critical

Private downloads are currently broken in the 7.x branch because of this, and we have no tests to prove it.

#17

Dries - July 18, 2008 - 07:19
Status:reviewed & tested by the community» needs work

I've just committed the bug fix but we still need tests for it.

#18

webchick - July 18, 2008 - 07:49
Component:upload.module» tests

Moving to "tests" component for more visibility.

#19

Damien Tournoud - July 18, 2008 - 07:54

@webchick: shouldn't we try to get this to 6 before (priority-wise)?

#20

catch - July 18, 2008 - 08:54
Version:7.x-dev» 6.x-dev
Component:tests» upload.module
Status:needs work» patch (to be ported)

Moving back to 6.x, looks like we don't have a committable patch for that branch yet.

#21

catch - July 18, 2008 - 09:29
Title:Tests needed: upload.module hard-codes 'view uploaded files' permission check» Upload.module hard-codes 'view uploaded files' permission check

I've opened a separate issue for tests so we can concentrate on the Drupal 6 breakage here: #284269: Tests for private file transfers

#22

drewish - July 25, 2008 - 02:02
Status:patch (to be ported)» reviewed & tested by the community

here's HEAD's patch for D6.

AttachmentSizeStatusTest resultOperations
upload_247095.patch1.73 KBIdleFailed: Failed to apply patch.View details

#23

Gábor Hojtsy - September 17, 2008 - 06:13
Version:6.x-dev» 7.x-dev
Status:reviewed & tested by the community» needs work

Did some research and found that people experienced the same bug in 2006 and it was fixed in the then current version (4.7.x): http://drupal.org/node/59648 (looks like it was not forward ported).

Anyway Drupal 6.4 was released with this fix in there, which seems like a better idea:

<?php
/**
* Implementation of hook_file_download().
*/
function upload_file_download($filepath) {
 
$filepath = file_create_path($filepath);
 
$result = db_query("SELECT f.*, u.nid FROM {files} f INNER JOIN {upload} u ON f.fid = u.fid WHERE filepath = '%s'", $filepath);
  if (
$file = db_fetch_object($result)) {
    if (
user_access('view uploaded files') && ($node = node_load($file->nid)) && node_access('view', $node)) {
      return array(
       
'Content-Type: ' . $file->filemime,
       
'Content-Length: ' . $file->filesize,
      );
    }
    else {
      return -
1;
    }
  }
}
?>

Note that it does not only check whether the user has the 'view uploaded files' permission, it also checks, whether the user has access to the related node. There are other fixes released in the 6.4 security release, which could be missing from Drupal 7: http://cvs.drupal.org/viewvc.py/drupal/drupal/modules/upload/upload.modu...

BTW there is already an open issue for this node_access() check with the use case of assigning the upload to multiple nodes with different access checks: http://drupal.org/node/295586 so it is not the best end of all things.

#24

drewish - January 12, 2009 - 08:02
Status:needs work» needs review

here's a patch based on Gábor's code from D6.

AttachmentSizeStatusTest resultOperations
upload_247095.patch818 bytesIdleFailed: 8866 passes, 1 fail, 0 exceptionsView details

#25

System Message - January 13, 2009 - 14:35
Status:needs review» needs work

The last submitted patch failed testing.

#26

quicksketch - February 8, 2009 - 22:30

We really, really need tests for this. #352236: Finish moving upload.module to DB:TNG broke the upload permissions again. I've created a new issue to fix the problem, #371206: upload_file_download() Blocks Access to Files Upload Doesn't Own.

#27

quicksketch - February 9, 2009 - 00:44
Status:needs work» needs review

Hmm, seems like my patch linked in #26 is the same problem drewish fixes in #24. Either patch will work fine since they're identical in implementation.

Here's a suite for testing private downloads and upload module. Tests the following cases:
- Privileged user is allowed to view files uploaded through upload module.
- Privileged user is not prevented from viewing files not uploaded through upload module.
- Privileged user is not allowed to view an unmanaged file (since no module is available to set headers).
- Unprivileged user is not allowed to view files uploaded through upload module.

I've wrapped back in #371206: upload_file_download() Blocks Access to Files Upload Doesn't Own to make it so that the tests pass, since otherwise they wouldn't as upload.module currently has the bug reported in that issue.

I'll be happy to never see this problem again.

AttachmentSizeStatusTest resultOperations
upload_permissions_test.patch4.41 KBIdleFailed: Failed to apply patch.View details

#28

quicksketch - February 9, 2009 - 00:57

My test for checking "Access Denied" on unmanaged files was backward (I was checking for 200 instead of 404). This patch correctly tests unmanaged files and fixes a spacing issue.

AttachmentSizeStatusTest resultOperations
upload_permissions_test.patch4.41 KBIdleFailed: Failed to apply patch.View details

#29

System Message - February 9, 2009 - 01:20
Status:needs review» needs work

The last submitted patch failed testing.

#30

c960657 - August 19, 2009 - 18:52
Status:needs work» needs review

Updated patch. Also fixes two regressions introduced in #517814: File API Stream Wrapper Conversion (1: uploaded files were always made public, and 2: wrong variable name used in file_build_uri()).

AttachmentSizeStatusTest resultOperations
upload_file_download-1.patch7.56 KBIdleFailed: Failed to apply patch.View details

#31

drewish - August 20, 2009 - 03:23

these changes look really good but i'd like to make this query a bit more specific so it doesn't look like it's replicating file_load():

+  $file = db_query("SELECT f.*, u.nid FROM {file} f INNER JOIN {upload} u ON f.fid = u.fid WHERE uri = :uri", array(':uri' => $uri))->fetchObject();

to:
+  $file = db_query("SELECT f.filemime, f.filesize, u.nid FROM {file} f INNER JOIN {upload} u ON f.fid = u.fid WHERE uri = :uri", array(':uri' => $uri))->fetchObject();

#32

c960657 - August 20, 2009 - 16:09

Done.

AttachmentSizeStatusTest resultOperations
upload_file_download-2.patch9.25 KBIdleFailed: 12097 passes, 1 fail, 2 exceptionsView details

#33

c960657 - August 20, 2009 - 16:19

Sorry, this is better.

AttachmentSizeStatusTest resultOperations
upload_file_download-3.patch9.26 KBIdleFailed: Failed to apply patch.View details

#34

c960657 - August 20, 2009 - 16:22

And this is even better.

AttachmentSizeStatusTest resultOperations
upload_file_download-4.patch7.54 KBIdleFailed: Failed to apply patch.View details

#35

System Message - August 21, 2009 - 16:55
Status:needs review» needs work

The last submitted patch failed testing.

#36

c960657 - August 22, 2009 - 01:22
Status:needs work» needs review

Reroll.

AttachmentSizeStatusTest resultOperations
upload_file_download-5.patch7.63 KBIdleFailed: Failed to apply patch.View details

#37

System Message - August 23, 2009 - 03:10
Status:needs review» needs work

The last submitted patch failed testing.

#38

c960657 - August 23, 2009 - 08:02
Status:needs work» needs review

A testbot glitch?

#39

roychri - August 31, 2009 - 21:35
Status:needs review» reviewed & tested by the community

This patch works for me.

I tried running the upload test in the patch without the actual changes and I get failures.
Running the upload test with the full patch runs without any problem.

#40

System Message - September 22, 2009 - 19:18
Status:reviewed & tested by the community» needs work

The last submitted patch failed testing.

#41

c960657 - September 25, 2009 - 15:51
Status:needs work» needs review

The tests broke due to an accidental commit in #118345: Revamp hook_user_form/_register/_validate/_submit/_insert/_update (see #38).

#42

System Message - October 20, 2009 - 14:02
Status:needs review» needs work

The last submitted patch failed testing.

#43

c960657 - October 20, 2009 - 16:15
Status:needs work» needs review

Reroll.

AttachmentSizeStatusTest resultOperations
upload_file_download-6.patch7.56 KBTest request sentPassed: 14458 passes, 0 fails, 0 exceptionsView details

#44

webchick - October 20, 2009 - 19:11
Version:7.x-dev» 6.x-dev

I think I would rather fix this in 7.x with #563000: Remove upload module and provide upgrade path to file. So, assuming that patch makes it in, this is a 6.x-only bug.

#45

System Message - December 15, 2009 - 04:39

sterwa requested that failed test be re-tested.

#46

c960657 - December 16, 2009 - 17:19
Status:needs review» fixed

The D6 issue was fixed in this commit:
http://drupalcode.org/viewvc/drupal/drupal/modules/upload/upload.module?...

There CVS commit message was simply “Drupal 6.4.”, i.e. there was no issue reference. The 6.4 release notes refers to SA-2008-047 that mentions “various Upload module vulnerabilities”.

#47

System Message - December 30, 2009 - 17:20
Status:fixed» closed

Automatically closed -- issue fixed for 2 weeks with no activity.

 
 

Drupal is a registered trademark of Dries Buytaert.