在 Rust 中編寫共用程式庫

https://ericchiang.github.io/post/rust-libs/

撰寫 Rust 共享庫

2024/02/26

當工具變得足夠龐大時,最終都會提供支援第三方邏輯的方式。或許您會開放 API 供客戶調用,或者執行某些程式碼以運行在沙箱內,又或者嵌入 Lua 直譯器。

對許多程式而言,可擴展性意味著動態共享庫。傳統的「這是一個供你進行 dlopen()的.so 檔案」。PKCS#11、Sudo 插件、Python 和 NodeJS 插件、SQLite 和 Postgres 擴展、Nginx 和 httpd 模組,甚至 LD_PRELOAD 黑客都是如此。

盡管 C 很有趣,但最近有一些項目利用了 Rust 的 FFI 支援,這引起了我的關注。這篇文章介紹了一個 Google Authenticator*的概念驗證 Linux-PAM 模塊,並介紹了使用 Rust 建立共享庫的步驟。

*當然,Google 在 GitHub 組織下維護了一個實際的模組,但這樣不就沒有樂趣了嗎?

可插入身分驗證模組

Linux-PAM 是一套積極運用共享庫的系統。即使是預設,PAM 邏輯都是實現為共享物件而不是直接使用核心庫。例如,我 Debian 機器上的默認 PAM 配置是設置為調用 pam_unix.so,該模組執行了大部分作業。

Linux-PAM 身分證驗證服務預期導出以下 API。

int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv);
int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv);

在 C 和 Rust 之間的配合

首先,建立一個 Rust 庫。

cargo new –lib pam_totp

封裝的設定將定義一些計算 TOTP 代碼的相依庫、C 綁定工具,以及將其宣告為 C 共享庫。

要告訴 bindgen 生成什麼,請定義一個包括專案所需 PAM 標頭的標頭檔。

該項目將需要使用某些 PAM 功能,而不僅僅是導出符號,因此它還需要鏈接到 PAM 庫以引入其實作。

Debian 上需要使用 clang 和 PAM 開發標頭。

sudo apt-get install -y clang libpam0g-dev

編譯後,生成的 Rust 代碼將位於專案的 debug 目錄中。

要安裝,請將共享庫複製到 Linux-PAM 模組路徑。

如果「/lib/x86_64-linux-gnu/security/pam_totp.so」存在,則首先取消鏈接,以修復按道路中止問題(例如 sudo 在覆寫文件時中止)。

sudo unlink /lib/x86_64-linux-gnu/security/pam_totp.so
sudo cp target/release/libpam_totp.so /lib/x86_64-linux-gnu/security/pam_totp.so

並將 TOTP 密碼以 base32 格式寫入/etc。

這樣一來,當切換到「root」用戶時,su 將會提示我輸入一個 TOTP 代碼。

ericchiang@localhost:~$ su –
輸入 Google Authenticator 代碼:
Google Authenticator 代碼匹配
root@localhost:~$

當然,如果代碼錯誤,它將拒絕我。

ericchiang@localhost:~$ su –
輸入 Google Authenticator 代碼:
pam 模組:驗證失敗:無效的 totp 代碼
su:身分驗證失敗
ericchiang@localhost:~$

成功!

為什麼 Rust?

我在工作中寫的大部分 C 代碼都是針對這些類型的插件。雖然手動跟蹤配置和「知道你仍然有它的」是很有趣的練習,但在程式碼檢閱期間試圖深入了解別人的記憶管理可能很麻煩。我曾在一個最不喜歡的專案中重構一個 C ASN.1 解析器。

Rust 最終成為了系統程式設計的一種優秀且現代化的語言。在以前我會傾向於編寫一個 C 外殼來執行其他事物的 PAM 等情況下,Rust 為整合和編寫自定邏輯提供了堅固的方式。無論是 OS 工具、驅動程式,還是只是一個 PAM 擴充,Rust 都提供了優良的支援。

總計 50 詞。

via Hacker News

March 25, 2024 at 08:44PM

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *