車輪の再発明

いったい何番煎じだよ!

ftpコマンドで記号を含むパスワードを使う話

ftpのログインがはじかれる

同僚が何やらはまっている様子だったので聞いてみると、こんな感じのシェルスクリプトでパスワードにバックスラッシュが含まれているとログインに失敗するんだとか。

#そもそもパスワードにバックスラッシュってのはどうなんだ、、、

#!/bin/bash

$FTP_SERVER='192.168.0.1'          #FTPサーバのIP
$USER='ftpuser'                    #ユーザー名
$PASS='aaa\bbb'                    #パスワード
$FILE='/foo/bar.tgz'               #ダウンロードするファイル

ftp -n $FTP_SERVER << EOF
user $USER $PASS
put $FILE
bye
EOF

でも、ftpコマンドでuserを使わないで対話的にバックスラッシュを含むパスワードを指定するとうまくいく。 こんな感じ。

[user@localhost tmp]$ ftp 127.0.0.1
Connected to 127.0.0.1 (127.0.0.1). 
220 (vsFTPd 3.0.2) 
Name (localhost:root): ftpuser
331 Please specify the password. 
Password:                          ←aaa\bbbを入力
230 Login successful. 
Remote system type is UNIX. 
Using binary mode to transfer files. 
ftp>

困ったときのtcpdump

というわけでtcpdumpしてみると、、、

[user@localhost tmp]$sudo tcpdump -i lo -s 0 -nn -n -X port 21
~~~中略~~~
03:06:17.816675 IP 127.0.0.1.34702 > 127.0.0.1.21: Flags [P.], seq 49:62, ack 149, win 342, options [nop,nop,TS val 1087324921 ecr 1087324921], length 13: FTP: PASS aaabbb
        0x0000:  4510 0041 4fb6 4000 4006 ecee 7f00 0001  E..AO.@.@.......
        0x0010:  7f00 0001 878e 0015 68b8 e838 42c7 b48c  ........h..8B...
        0x0020:  8018 0156 fe35 0000 0101 080a 40cf 42f9  ...V.5......@.B.
        0x0030:  40cf 42f9 5041 5353 2061 6161 6262 620d  @.B.PASS.aaabbb.
        0x0040:  0a

バックスラッシュがいない!!

ちなみに、対話的に実行した場合は下記。バックスラッシュもいる。

[user@localhost tmp]$sudo tcpdump -i lo -s 0 -nn -n -X port 21
~~~中略~~~
03:06:00.160534 IP 127.0.0.1.34702 > 127.0.0.1.21: Flags [P.], seq 15:29, ack 55, win 342, options [nop,nop,TS val 1087307265 ecr 1087301873], length 14: FTP: PASS aaa\bbb
        0x0000:  4510 0042 4fb0 4000 4006 ecf3 7f00 0001  E..BO.@.@.......
        0x0010:  7f00 0001 878e 0015 68b8 e816 42c7 b42e  ........h...B...
        0x0020:  8018 0156 fe36 0000 0101 080a 40ce fe01  ...V.6......@...
        0x0030:  40ce e8f1 5041 5353 2061 6161 5c62 6262  @...PASS.aaa\bbb
        0x0040:  0d0a

ソースを読んでみる

ソースを入手

ソースをyumって、、、

[user@localhost tmp]$ yumdownloader --source ftp
Loaded plugins: fastestmirror, kabi, langpacks, versionlock
Loading support for Red Hat kernel ABI
Enabling updates-source repository
Enabling base-source repository
Enabling extras-source repository
Loading mirror speeds from cached hostfile
 * base: ftp.iij.ad.jp
 * extras: ftp.iij.ad.jp
 * updates: ftp.iij.ad.jp
ftp-0.17-67.el7.src.rpm                                                                                                                                                                                               |  96 kB  00:00:01
[user@localhost tmp]$ ll
total 96
-rw-rw-r-- 1 user user 98263 Dec 16  2016 ftp-0.17-67.el7.src.rpm
[user@localhost tmp]$

展開。

[user@localhost tmp]$ rpm2cpio ftp-0.17-67.el7.src.rpm |cpio -id
279 blocks
[user@localhost tmp]$ tar zxf netkit-ftp-0.17.tar.gz
[user@localhost tmp]$ cd netkit-ftp-0.17/ftp/
[user@localhost tmp]$ ll
total 200
-rw-r--r-- 1 user user   672 Aug  1  1999 Makefile
-rw-r--r-- 1 user user 46570 Jul 23  2000 cmds.c
-rw-r--r-- 1 user user  1836 Aug 15  1996 cmds.h
-rw-r--r-- 1 user user 10376 Sep 29  1999 cmdtab.c
-rw-r--r-- 1 user user  4209 Aug 15  1996 domacro.c
-rw-r--r-- 1 user user 27271 Jul 31  2000 ftp.1
-rw-r--r-- 1 user user 35673 Dec 14  1999 ftp.c
-rw-r--r-- 1 user user  6770 Oct  3  1999 ftp_var.h
-rw-r--r-- 1 user user 12836 Oct  2  1999 glob.c
-rw-r--r-- 1 user user  1971 Aug 15  1996 glob.h
-rw-r--r-- 1 user user 11812 Oct  2  1999 main.c
-rw-r--r-- 1 user user  4657 Jul 31  2000 netrc.5
-rw-r--r-- 1 user user  2008 Jul 14  1996 pathnames.h
-rw-r--r-- 1 user user  7496 Oct  3  1999 ruserpass.c
[user@localhost tmp]$

読む

ftpコマンドのプロンプトへの入力はGNU readlineで読み込まれるようになっている。 GNU readlineはbashみたいにショートカットキーやhistoryを簡単に使えるようになるとのこと。 つまり、bashのようにバックスラッシュはエスケープが必要だった模様。

#main.c 279行目~

static char *get_input_line(char *buf, int buflen)
{
#ifdef __USE_READLINE__
        if (fromatty && !rl_inhibit) {
                char *lineread = readline("ftp> ");   #ここ!!
                if (!lineread) return NULL;
                strncpy(buf, lineread, buflen);
                buf[buflen-1] = 0;
                if (lineread[0]) add_history(lineread);
                free(lineread);
                return buf;
        }
#endif
        if (fromatty) {
                printf("ftp> ");
                fflush(stdout);
        }
        return fgets(buf, buflen, stdin);
}

ちなみに、userを使用せず対話的にパスワードを入力すると、下記のようにgetpass(3)が呼ばれる。 こいつは自分でttyを直接読みに行ってくれるようで、エスケープは不要だったというわけ。

#cmds.c 1523行目~

void
user(int argc, char *argv[])
{
        char theacct[80];
        int n, aflag = 0;

        if (argc < 2)
                (void) another(&argc, &argv, "username");
        if (argc < 2 || argc > 4) {
                printf("usage: %s username [password] [account]\n", argv[0]);
                code = -1;
                return;
        }
        n = command("USER %s", argv[1]);
        if (n == CONTINUE) {
                if (argc < 3 )
                        argv[2] = getpass("Password: "), argc++;    #ここ!!
                n = command("PASS %s", argv[2]);
        }
        if (n == CONTINUE) {
                if (argc < 4) {
                        printf("Account: "); (void) fflush(stdout);
                        fgets(theacct, sizeof(theacct), stdin);
                        argv[3] = theacct; argc++;
                }
                n = command("ACCT %s", argv[3]);
                aflag++;
        }
        if (n != COMPLETE) {
                fprintf(stdout, "Login failed.\n");
                return;
        }
        if (!aflag && argc == 4) {
                (void) command("ACCT %s", argv[3]);
        }
}

エスケープしてあげると、、、

できた。

[user@localhost tmp]$ ftp -n 127.0.0.1
Connected to 127.0.0.1 (127.0.0.1).
220 (vsFTPd 3.0.2)
ftp> user ftpuser aaa\\bbb
331 Please specify the password.
230 Login successful. 
Remote system type is UNIX. 
Using binary mode to transfer files. 
ftp>
[user@localhost tmp]$sudo tcpdump -i lo -s 0 -nn -n -X port 21
~~~中略~~~
03:48:15.782473 IP 127.0.0.1.34874 > 127.0.0.1.21: Flags [P.], seq 21:35, ack 93, win 342, options [nop,nop,TS val 1089842887 ecr 1089842887], length 14: FTP: PASS aaa\bbb
        0x0000:  4510 0042 9281 4000 4006 aa22 7f00 0001  E..B..@.@.."....
        0x0010:  7f00 0001 883a 0015 c770 e9ed 5b4e 3615  .....:...p..[N6.
        0x0020:  8018 0156 fe36 0000 0101 080a 40f5 aec7  ...V.6......@...
        0x0030:  40f5 aec7 5041 5353 2061 6161 5c62 6262  @...PASS.aaa\bbb
        0x0040:  0d0a

まあエスケープくらいわざわざソース読まなくても思いつけって話ですよね、、、