C/C++ 中 system() 呼叫的可繼承能力
目前我正在嘗試通過閱讀http://man7.org/linux/man-pages/man7/capabilities.7.html來了解 Linux 中的功能
我創建了一個具有該功能的小型 C++ 應用程序
CAP_DAC_READ_SEARCH+eip
該功能適用於應用程序。但我有一個
system()
電話在裡面system("cat /dev/mtdX > targetFile");
我怎樣才能繼承這個呼叫的能力?
編輯:
我知道這
system()
是由fork()
+驅動的execl()
。在文件中提到,fork()
子程序獲得與父程序相同的功能。但是為什麼不繼承讀取能力呢?
感謝@mosvy,我用 libcap 實現了他的解決方案,它似乎按預期工作。
void inheritCapabilities() { cap_t caps; caps = cap_get_proc(); if (caps == NULL) throw "Failed to load capabilities"; printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL)); cap_value_t cap_list[1]; cap_list[0] = CAP_DAC_READ_SEARCH; if (cap_set_flag(caps, CAP_INHERITABLE, 1, cap_list, CAP_SET) == -1) throw "Failed to set inheritable"; printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL)); if (cap_set_proc(caps) == -1) throw "Failed to set proc"; printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL)); caps = cap_get_proc(); if (caps == NULL) throw "Failed to load capabilities"; printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL)); if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_DAC_READ_SEARCH, 0, 0) == -1) throw "Failed to pr_cap_ambient_raise! Error: " + errno; } main() { inheritCapabilities(); char *catargv[5]; catargv[0] = (char *)"cmd"; catargv[1] = (char *)"arg1"; catargv[2] = (char *)"arg2"; catargv[3] = (char *)"arg3"; catargv[4] = NULL; if (execvp(catargv[0], catargv) == -1) throw "Failed! command"; }
首先,你應該讓
system(3)
開;與您的建議不同的system(3)
是,不僅是fork+exec
,而且是相當複雜的事情,涉及更改信號配置,等待孩子並/bin/sh
用作包裝器(根據其維護者的突發奇想和假設,可能會刪除或添加功能,弄亂環境變數,來源初始化腳本和其他有趣的東西)。使用 justexecv*(2)
而不是system(3)
將消除所有那些虛假的並發症。其次,您應該深入了解聯機幫助頁中的“能力轉換期間
execve()
”部分。capabilities(7)
我不打算在這裡複製粘貼它,但它基本上歸結為:除非將它們添加到執行緒(程序)的環境集中,否則不會通過 execve() 繼承功能,並且它們不能在那裡添加,除非它們已經在執行緒的可繼承集中。(文件元數據中的“可繼承”功能只是一個遮罩,限制了執行緒的功能)。因此,為了通過
execve()
您繼承的功能應該**a)**將它們從允許的集合複製到可繼承的集合(您可以使用capset(2)
系統呼叫來完成$$ 1 $$) 和**b)**將它們添加到環境集(您可以使用
prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE)
)。 把它們放在一起:$ cat capexec.c #include <sys/prctl.h> #include <unistd.h> #include <sys/syscall.h> #include <linux/capability.h> #include <err.h> int main(int ac, char **av){ static char *dav[] = { "/bin/bash", 0 }; struct __user_cap_header_struct hs; struct __user_cap_data_struct ds[2]; hs.version = 0x20080522; /*_LINUX_CAPABILITY_VERSION_3;*/ hs.pid = getpid(); if(syscall(SYS_capget, &hs, ds)) err(1, "capget"); ds[0].inheritable = ds[0].permitted; if(syscall(SYS_capset, &hs, ds)) err(1, "capset"); if(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_DAC_READ_SEARCH, 0, 0)) err(1, "prctl(pr_cap_ambient_raise)"); av = ac < 2 ? dav : av + 1; execvp(*av, av); err(1, "execvp %s", *av); } $ cc -Wall capexec.c -o capexec # as root # setcap cap_dac_read_search+ip /tmp/capexec $ ./capexec dd if=/dev/sda of=/dev/null count=1 1+0 records in 1+0 records out 512 bytes copied, 0.000299173 s, 1.7 MB/s
$$ 1 $$文件建議使用 libcap 庫;這個範例的部分內容是從我為舊版本的 android 編寫的 hack 中提取的,其中沒有 libcap,並且缺少許多標頭定義。將其轉換為使用 libcap 包裝器留給讀者作為練習。