--- title: 修改 vsftpd source 以支援 MFMT 指令 tags: linux --- # 修改 vsftpd source 以支援 MFMT 指令 ## 為何要做這件事 最近實驗室伺服器主機在重整,舊主機的FTP服務使用 [proftpd](http://www.proftpd.org/) ,因為安全疑慮以及使用者難以管理,所以改用了 [vsftpd](https://security.appspot.com/vsftpd.html) 來提供。 實驗室會使用客戶端軟體[Filezilla](https://filezilla-project.org/),來連上FTP,並會啟用 "保留時間戳記" 這個功能,讓上傳到 FTP 的檔案時間戳記是本地端最後修改的時間,而非上傳時間。 經過測試後,發現"Filezilla 保留時間戳記"功能無法正常執行,於是有了後續的一連串測試及實驗。 ## 1. MFMT 與 MDTM 的戰爭 在Filezilla啟用"保留時間戳記"功能時,會先跳出此視窗,提示服務器端需要支援 MFMT 才行。 ![](https://i.imgur.com/5Xhlk6c.png) 於是我們搜索了 "vsftpd mfmt" ,想要啟用 MFMT 的功能。 然後我們先看到這篇討論 https://forum.filezilla-project.org/viewtopic.php?t=48868 裡面提及了`mdtm_write=YES`,可以透過啟用此參數,讓 MDMT 指令可以修改檔案時間,於是我們就去嘗試看看了,結果用Filezilla一樣無法完成此功能。 但是若透過直接下達指令是可以更改時間戳記的 ``` 指令: MDTM 20190301000000 test.txt 回應: 213 File modification time set. ``` ![](https://i.imgur.com/LGjkUFM.png) 再仔細看了一下討論區,發現裡面說了 Filezilla 是使用 MFMT 的指令來完成此功能。有人提議說讓 Filezilla 支援 MDTM 來完成此功能,但是開發者說 MDTM 指令是拿來取得檔案時間戳記的指令,不應該拿來改時間,一種指令只應該支援一種功能。 後續又繼續搜索了一下,發現了好幾篇文章都在討論同樣的問題,都是客戶端軟體只支援`MFMT`而vsftpd 只支援`MDTM`,有些人想要客戶端軟體支援`MDTM`,有些人想要vsftpd 支援`MFMT`,但都被另一邊拒絕了。而此問題從2009就在討論了一直到2019都還找的到新文章在吵 XD。像下面兩篇: 1. https://freefilesync.org/forum/viewtopic.php?t=6336 2. https://www.linuxquestions.org/questions/linux-server-73/vsftpd-keep-client-timestamp-on-uploaded-file-695831/ - - - 後來整件事情處理完後回來看一些規範。 在 [wiki List_of_FTP_commands](https://en.wikipedia.org/wiki/List_of_FTP_commands) 中,寫著`MDTM`指令是用來取得時間的,`MFMT`指令是來修改時間的。 ![](https://i.imgur.com/yFjRECX.png) 但是看wiki給的標準參照[RFC3659](https://tools.ietf.org/html/rfc3659),裡面說,似乎是因應 [REST](https://zh.wikipedia.org/wiki/%E8%A1%A8%E7%8E%B0%E5%B1%82%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2) 的風格,所以讓此命令有PUT及GET的功能,進而有了修改時間以及取得時間兩種功能。 ## 修改 vsftpd 的 source 在發現客戶端與服務端使用兩種不同指令後,我們開始思考何不修改其中一邊的程式呢,因為`MFMT`與`MDTM`兩種命令的格式一模一樣,所以修改起來應該不會太困難,只需要搜索到命令的字串然後去修改相關代碼就好。因為客戶端需要發布給使用者,還有一堆問題要處理,會比較麻煩,所以決定去修改`vsftpd`。 linux 操作部分主要參考以下兩篇: 1. [How do I get and modify the source code of packages installed through apt-get? ](https://askubuntu.com/questions/28372/how-do-i-get-and-modify-the-source-code-of-packages-installed-through-apt-get?fbclid=IwAR0Ok62bRWRlVLW8R_3YIdmlhb5EpKX49kLsmSZd_Bo3imOMMPxrd0VYdxs) 2. [Error :: You must put some 'source' URIs in your sources.list ](https://askubuntu.com/questions/496549/error-you-must-put-some-source-uris-in-your-sources-list) ### 1. 取得原始碼 將 /etc/apt/sources.list 中 `deb-src` 行首的註解 `#` 移除,改成如下,讓 apt 知道要到哪邊 search 原始碼。 ``` # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to # newer versions of the distribution. deb http://tw.archive.ubuntu.com/ubuntu/ bionic main restricted deb-src http://tw.archive.ubuntu.com/ubuntu/ bionic main restricted ``` 下達指令叫 apt 依據 /etc/apt/sources.list 去取得新的套件列表,同時,也會將套件的原始碼下載處增加到套件列表中。 ``` sudo apt-get update ``` 下達指令下載 vsftpd 的原始碼,這邊我都是在家目錄 `~` 底下操作,注意這邊不需要 sudo 因為指示將檔案下載下來而已,下載完後原始碼放在 `~/vsftpd-3.0.3` 中。 ``` apt-get source vsftpd ``` ![](https://i.imgur.com/493WbxM.png) 切換到原始碼資料夾中。 ``` cd vsftpd-3.0.3 ``` ### 2. 修改 vsftpd 以支援 MFMT 因為要加`MFMT`指令支援到代碼中,而`MDTM`指令與它的功能幾乎一模一樣,所以我決定先追蹤`MDTM`指令是如何實現的。 我猜測會有一個命令分發的地方,而這個地方會去比對所有命令的字串,在執行不同的動作,所以應該會有一個`MDTM`的字串,在原碼中搜索。 看到這邊命令比較到`MDTM`後,就會呼叫`handle_mdtm(p_sess);`。 https://github.com/dagwieers/vsftpd/blob/master/postlogin.c#L318-L321 ![](https://i.imgur.com/Er4czd9.png) 看到函式原型`handle_mdtm(struct vsf_session* p_sess)`,猜測 p_sess 是保存當前 FTP 當前 session 的所有資訊,而一長串的命令也會經過parser後放入此結構中。 像命令就會放在 `&p_sess->ftp_cmd_str` 中。 看了一下 `handle_mdtm` 的原始碼,覺得`MFMT`的功能也可以只透過這個函式實現。 在 318 行後加入以下代碼,讓`MFMT`指令分發後也執行`handle_mdtm`函式。 ```c=318 else if (str_equal_text(&p_sess->ftp_cmd_str, "MDTM")) { handle_mdtm(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "MFMT")) { handle_mdtm(p_sess); } ``` ### 3. 編譯並安裝 備份 vsftpd 設定,這邊備份到家目錄底下 ``` cp /etc/vsftpd.conf ~/vsftpd.conf ``` 安裝建置工具,並安裝建置vsftpd相依的套件。 ``` sudo apt-get install dpkg sudo apt-get install devscripts sudo apt-get build-dep vsftpd ``` 建置 vsftpd 囉。 ``` debuild -b -uc -us ``` 輸出: ``` dpkg-deb: building package 'vsftpd' in '../vsftpd_3.0.3-9build1_amd64.deb'. ``` 解除安裝舊套件,然後安裝剛剛建置好的。 ``` sudo apt-get remove vsftpd sudo dpkg -i ../vsftpd_3.0.3-9build1_amd64.deb ``` 還原 vsftpd 設定,然後啟動 vsftpd 。 ``` sudo cp ~/vsftpd.conf /etc/vsftpd.conf sudo systemctl start vsftpd sudo systemctl status vsftpd ``` ### 3. 修改 vsftpd 的 FEAT 命令 接著我們在進行測試,直接上傳檔案依樣不能保留時間戳記,但是直接下達指令可以。猜測是 Filezilla 連上伺服器會先用`FEAT`命令得到此伺服器支援那些命令,再用這些指令去作檔案傳輸。 ![](https://i.imgur.com/3eWKeMq.png) 重開Filezilla,重新連上伺服器,果然會使用`FEAT`獲取可用命令。 ![](https://i.imgur.com/iy680bk.png) 修改 features.c 中的 handle_feat 函式,讓它告知客戶端說`MFMT`是可用指令,再執行 3. 編譯並安裝 一次。 https://github.com/dagwieers/vsftpd/blob/master/features.c#L38 ``` vsf_cmdio_write_raw(p_sess, " MDTM\r\n"); vsf_cmdio_write_raw(p_sess, " MFMT\r\n"); ``` 修改後就完成讓vsftpd支援`MFMT`指令了。 ## 後記 後來想想為何能在這麼短時間內修改完,linux上的操作主要歸功於網路上的資源,在修改程式碼方面,vsftpd的程式設計得很好,原先就對 FTP 協定有一定的了解了,再加上滿常看一些程式,所以追蹤起來馬上就找到需要的片段了,讀起來也不會太吃力。 ## 20200322 補充 根據 [The "MFMT", "MFCT", and "MFF" Command Extensions for FTP](https://www.ietf.org/archive/id/draft-somers-ftp-mfxx-04.txt) 的網路草案,提出了"MFMT", "MFCT", and "MFF"三個指令,其中`MFMT`設計用來取代原先的`MDTM`修改檔案時間戳記的功能,並建議廢止`MDTM`的修改功能,讓`MDTM`只有讀取檔案時間戳記的功能。但這份草案似乎沒有被正式採用,所以有許多的FTP服務沒有支援此功能。 ![](https://i.imgur.com/dqR9aBw.png) ## 20200330 補充 我把專案放到 github 了,可以直接clone此專案安裝。 https://github.com/mickey9910326/vsftpd-with-MFMT ## 備註 ### Filezilla 開啟詳細記錄 右鍵->顯示詳細日誌 ![](https://i.imgur.com/OzynjMy.png) ### Filezilla 輸入指令 右鍵->輸入自訂指令 ![](https://i.imgur.com/yj7PeIq.png) ### github 上非官方原碼 https://github.com/dagwieers/vsftpd