img_auth.php 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php
  2. /**
  3. * Image authorisation script
  4. *
  5. * To use this:
  6. *
  7. * - Set $wgUploadDirectory to a non-public directory (not web accessible)
  8. * - Set $wgUploadPath to point to this file
  9. *
  10. * Your server needs to support PATH_INFO; CGI-based configurations
  11. * usually don't.
  12. *
  13. * @file
  14. */
  15. define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
  16. require_once( dirname( __FILE__ ) . '/includes/WebStart.php' );
  17. wfProfileIn( 'img_auth.php' );
  18. require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' );
  19. $perms = User::getGroupPermissions( array( '*' ) );
  20. if ( in_array( 'read', $perms, true ) ) {
  21. wfDebugLog( 'img_auth', 'Public wiki' );
  22. wfPublicError();
  23. }
  24. // Extract path and image information
  25. if( !isset( $_SERVER['PATH_INFO'] ) ) {
  26. wfDebugLog( 'img_auth', 'Missing PATH_INFO' );
  27. wfForbidden();
  28. }
  29. $path = $_SERVER['PATH_INFO'];
  30. $filename = realpath( $wgUploadDirectory . $_SERVER['PATH_INFO'] );
  31. $realUpload = realpath( $wgUploadDirectory );
  32. wfDebugLog( 'img_auth', "\$path is {$path}" );
  33. wfDebugLog( 'img_auth', "\$filename is {$filename}" );
  34. // Basic directory traversal check
  35. if( substr( $filename, 0, strlen( $realUpload ) ) != $realUpload ) {
  36. wfDebugLog( 'img_auth', 'Requested path not in upload directory' );
  37. wfForbidden();
  38. }
  39. // Extract the file name and chop off the size specifier
  40. // (e.g. 120px-Foo.png => Foo.png)
  41. $name = wfBaseName( $path );
  42. if( preg_match( '!\d+px-(.*)!i', $name, $m ) )
  43. $name = $m[1];
  44. wfDebugLog( 'img_auth', "\$name is {$name}" );
  45. $title = Title::makeTitleSafe( NS_FILE, $name );
  46. if( !$title instanceof Title ) {
  47. wfDebugLog( 'img_auth', "Unable to construct a valid Title from `{$name}`" );
  48. wfForbidden();
  49. }
  50. $title = $title->getPrefixedText();
  51. // Check the whitelist if needed
  52. if( !$wgUser->getId() && ( !is_array( $wgWhitelistRead ) || !in_array( $title, $wgWhitelistRead ) ) ) {
  53. wfDebugLog( 'img_auth', "Not logged in and `{$title}` not in whitelist." );
  54. wfForbidden();
  55. }
  56. if( !file_exists( $filename ) ) {
  57. wfDebugLog( 'img_auth', "`{$filename}` does not exist" );
  58. wfForbidden();
  59. }
  60. if( is_dir( $filename ) ) {
  61. wfDebugLog( 'img_auth', "`{$filename}` is a directory" );
  62. wfForbidden();
  63. }
  64. // Stream the requested file
  65. wfDebugLog( 'img_auth', "Streaming `{$filename}`" );
  66. wfStreamFile( $filename, array( 'Cache-Control: private', 'Vary: Cookie' ) );
  67. wfLogProfilingData();
  68. /**
  69. * Issue a standard HTTP 403 Forbidden header and a basic
  70. * error message, then end the script
  71. */
  72. function wfForbidden() {
  73. header( 'HTTP/1.0 403 Forbidden' );
  74. header( 'Vary: Cookie' );
  75. header( 'Content-Type: text/html; charset=utf-8' );
  76. echo <<<ENDS
  77. <html>
  78. <body>
  79. <h1>Access Denied</h1>
  80. <p>You need to log in to access files on this server.</p>
  81. </body>
  82. </html>
  83. ENDS;
  84. wfLogProfilingData();
  85. exit();
  86. }
  87. /**
  88. * Show a 403 error for use when the wiki is public
  89. */
  90. function wfPublicError() {
  91. header( 'HTTP/1.0 403 Forbidden' );
  92. header( 'Content-Type: text/html; charset=utf-8' );
  93. echo <<<ENDS
  94. <html>
  95. <body>
  96. <h1>Access Denied</h1>
  97. <p>The function of img_auth.php is to output files from a private wiki. This wiki
  98. is configured as a public wiki. For optimal security, img_auth.php is disabled in
  99. this case.
  100. </p>
  101. </body>
  102. </html>
  103. ENDS;
  104. wfLogProfilingData();
  105. exit;
  106. }