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
まあエスケープくらいわざわざソース読まなくても思いつけって話ですよね、、、