2015年12月17日木曜日

Uber-Uploader 6.8.2 が気づいたら動かなくなっていた。どうやら CGI.pm の更新が原因みたい。

追記)2017/6に,FreeBSD で fine-uploader を使ってみた(その1)というのを書いた。 そこでは fine-uploader という JavaScript のライブラリを使ったファイルのアップローダーについて書いている。 Uber Uploader はすでに古く更新されていないので,fine-uploader など新しいアップロードライブラリを使うことをお薦めする。
Uber-Uploader 6.8.2 についてはUber-Uploader 6.8.2 の使い方(その1)で述べているので,参考にしてみて欲しい。

 Uber-Uploader 6.8.2 を使っているが,いつからかはわからないが 2015/12 に気づいたら動かなくなっていた。

症状としては,アップロードをしようとはするが,
  Failed to assign CGI temp directory: Inappropriate ioctl for device
と表示が出て止まってしまった。

 この表示を見る限り,cgi ルーチンが temporary directory 関連のエラーを起こしていそう,というがわかった。 しかし,それだけだと情報が足りないので,次に /var/log/httpd-error.log のログを見てみた。 すると,下記のようなエラーが表示された。
------------------------------------------------
private_tempfiles has been deprecated at /usr/local/lib/perl5/site_perl/CGI.pm line 3213.
Died on line 162 at /usr/xxxx/xxxx/uber_uploader/cgi-bin/ubr_upload.pl line 803.
[Thu Dec 17 17:48:24.527624 2015] [cgid:error] [pid 75620] (32)Broken pipe: [client aaa.bbb.ccc.ddd:57696] AH02651: Error writing request body to script usr/xxxx/xxxx/uber_uploader/cgi-bin/ubr_upload.pl, referer: http://xxx.yyy.zzz.jp/uber_uploader/
------------------------------------------------
 わかりにくいが,これは3行の文章で,1行目は CGI.pm の中で「private_tempfiles」は非推奨と言われているみたいだった。 2行目は ubr_upload.pl の 803 行目で止まった,と書いてある。 3行目はよくわからないが,とりあえず ubr_upload.pl でエラーが起こっている,ということを意味していることだけはわかった。

 いずれにせよ「ubr_upload.pl」がエラーを起こしているので,その中を見てみることにした。 最終的に「Failed to assign CGI temp directory: Inappropriate ioctl for device」と表示が出るので, ubr_upload.pl の中でその表示をしている部分を探してみた。 すると,156 行から 163 行にかけて,
        # Disable private temp files
        CGI::private_tempfiles(0);

        # Tell CGI.pm to use our directory based on upload id
        if($TempFile::TMPDIRECTORY){ $TempFile::TMPDIRECTORY = $temp_dir_id; }
        elsif($CGITempFile::TMPDIRECTORY){ $CGITempFile::TMPDIRECTORY = $temp_dir_id; }
        else{ &kak("Failed to assign CGI temp directory: $!", 1, __LINE__); }
という部分があった。
 どうやら,ここが問題らしい。 しかし,なぜ急にここがエラーになったかが最初わからなかった。 ただ「CGI.pm では private_tempfiles が非推奨になった」みたいなエラーがあったので,CGI.pm が更新されたのが原因だと推測できた。 そうなると,CPAN の CGI.pm のページを見ないといけない。

 2015/12/17 現在,CGI.pm は version 4.22 が最新みたいだった。 そこで,CGI モジュールのページを見てみた。 より具体的には CGI モジュールの Changes in temporary file handling (v4.05+)に記載がある。 その中に書いてあることは,細かい点まで理解しきれていないが,どうやら「CGI::File::Temp」を廃止して,「File::Temp」を利用するように変更した,ということみたいだった。 上記の 156 行から 163 行の後半部分では「$TempFile」という変数に,こちらで指定した temporary directory を代入するように書いてあるが, 「CGI::File::Temp」が廃止されたことで,この「$TempFile」という変数が定義されていないみたいだった。

 そこで File::Temp を見てみた。 すると「File::Temp」モジュールの中の「tempdir」という関数を ubr_upload.pl に import して,156 - 163 行の代わりに使えばいいみたいだった。 具体的には ubr_upload.pl の最初の方に
use File::Temp qw/tempdir/;
と記載し,さらに上記の 156-163 行を全て消して,代わりに
tempdir (DIR => $temp_dir_id);
と書いておくことで,うまく temporary directory の問題は回避できた。

 ちなみに ubr_upload.pl にエラーがある場合は,
ERROR: Failed to find flength file
と表示が出る。逆に言うと,上記のエラーが出た場合は ubr_upload.pl にエラーがある場合があると思っておいた方がいい。

 さらに,この問題と直接関係はないが,/var/log/httpd-error.log を見ていると,
CGI::param called in list context from /usr/xxxx/xxxx/uber_uploader/cgi-bin/ubr_upload.pl line 822, 
this can lead to vulnerabilities. See the warning in "Fetching the value or values of a single named parameter" 
at /usr/local/lib/perl5/site_perl/CGI.pm line 404.
というエラーが沢山出ていた。 これも CGI モジュールのページを見ると記載があった。 どうやら param() 関数は使い方によっては脆弱性の原因となるので,multi_param() 関数を使うなどで回避しなさい,とあった。 ubr_load.pl では 711 行目の
                my @post_values = $query->param($key);
でエラーを吐いていた。 そこで,この行の param を multi_param に置き換えるとエラーが消えた。

 ということで,最終的に以下のように patch を当てるとよかった。
--- ubr_upload.pl.original  2010-07-24 xx:yy:54.000000000 +0900
+++ ubr_upload.pl       2015-12-17 xx:yy:06.000000000 +0900
@@ -25,6 +25,8 @@
 #      along with Uber-Uploader. If not, see http://www.gnu.org/licenses/.
 #
 #**********************************************************************************************************************************
+#  2015/12/17 CGI.pm (v 4.22) caused "temporary directory" error. ==> modified
+#**********************************************************************************************************************************

 #**********************************************************************************************************************************
 #      ATTENTION: THE $TEMP_DIR AND $DATA_DELIMITER VALUES MUST BE DUPLICATED IN THE "UBR_INI.PHP" FILE
@@ -44,6 +46,9 @@
 use File::Copy;                                            # Module for moving uploaded files
 use File::Path;                                            # Module for creating and removing directories
 use IO::File;                                              # Module for file IO
+## =======================================================
+use File::Temp qw/tempdir/;                                # Module for temporary file and directory
+## =======================================================

 # Makes %ENV safer
 $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
@@ -153,13 +158,7 @@
     my $hook_query = CGI->new(\&hook, $hook_handle);
 }
 else{
-    # Disable private temp files
-    CGI::private_tempfiles(0);
-
-    # Tell CGI.pm to use our directory based on upload id
-    if($TempFile::TMPDIRECTORY){ $TempFile::TMPDIRECTORY = $temp_dir_id; }
-    elsif($CGITempFile::TMPDIRECTORY){ $CGITempFile::TMPDIRECTORY = $temp_dir_id; }
-    else{ &kak("Failed to assign CGI temp directory: $!", 1, __LINE__); }
+    tempdir (DIR => $temp_dir_id); # tempdir is a function which defined in File::Temp
 }

 # Timestamp start of upload
@@ -708,7 +707,7 @@

        #Write post values
        foreach my $key (@names){
-               my @post_values = $query->param($key);
+               my @post_values = $query->multi_param($key);

                foreach my $post_value (@post_values){
                        $post_value =~ s/&/&/g;

念の為に書いておくが,patch は unified 形式なので
patch -u  ubr_upload.pl < patch.20151217
のようにすればよい(もっと省略できるような気もするが…)

1 件のコメント:

rattus さんのコメント...

Thanks a big bunch for fixing this and posting it. Even though I cannot read your language, thanks to the patch I could fix my Uber-Uploader. Eventually, I'm going to ditch it but it's very nice to have it working again in the meantime.