2011-08-20 63 views
6

在C中,使用POSIX調用,如何確定路徑是否位於目標目錄中?如何確定路徑是否在目錄中? (POSIX)

例如,Web服務器的根目錄爲/srv,這是守護進程的getcwd()。 解析/index.html的請求時,將返回/srv/index.html的內容。

如何過濾出/srv以外的路徑請求?

/../etc/passwd/valid/../../etc/passwd, 等

分裂在/路徑和拒絕含有..將打破任何有效數組訪問/srv/valid/../index.html

有沒有一個規範的方式來做到這一點與系統調用?還是我需要手動走路徑和計算目錄深度?

+2

我認爲這是'chroot(2)'發明的原因! –

+0

@Carl Norum:如果你給某人有限的shell權限,chroot會更好。如果你不想限制你創建的程序的訪問權限,那麼比chroot有更好的選擇。 – Dani

回答

6

總是有realpath

真實路徑()功能要計算,從路徑由* FILE_NAME *,絕對路徑解析到同一個目錄項,其分辨率不涉及指出, '' ,'..'或符號鏈接。

然後比較realpath爲您提供了所需的根目錄並查看它們是否匹配。

您也可以在預先設定"/srv"之前通過展開雙點來清理文件名。在斜線上分割輸入路徑,並逐一穿過它。如果你得到一個"."然後刪除它,繼續前進;如果你得到一個"..",然後刪除它和以前的組件(注意不要超過列表中的第一個條目);如果您還有其他任何東西,請繼續閱讀下一個組件。然後在組件之間粘貼剩餘的斜線,並在前面加上"/srv/"。所以,如果有人給你"/valid/../../etc/passwd",你最終將得到"/srv/etc/passwd""/where/is/../pancakes/house"將最終爲"/srv/where/pancakes/house"

這樣,你不能讓外面"/srv"(除了通過當然是符號鏈接)和傳入"/../.."將是相同的"/"(就像一個普通的文件系統)。但是如果您擔心"/srv"下的符號,您仍然希望使用realpath

通過組件處理路徑名組件還可以讓您斷開您向外界展示的佈局與實際文件系統佈局之間的連接; "/this/that/other/thing"不需要映射到任何地方的實際"/srv/this/that/other/thing"文件,路徑可能只是某種數據庫中的鍵或某種函數調用的命名空間路徑。

0

您應該簡單地自己處理..,並在找到之前刪除之前的路徑組件,以便在用於打開文件的最終字符串中不會出現..

2

要確定文件F是否位於目錄D中,首先需要確定其設備號和inode編號(struct stat的st_dev和st_ino成員)的屬性D.

然後stat F來確定它是否是一個目錄。如果沒有,請調用basename來確定包含它的目錄的名稱。將G設置爲該目錄的名稱。如果F已經是目錄,則設置G = F。

現在,當且僅當G在D之內時,F在D之內。接下來我們有一個循環。

while (1) { 
    if (samefile(d_statinfo.d_dev, d_statinfo.d_ino, G)) { 
    return 1; // F was within D 
    } else if (0 == strcmp("/", G) { 
    return 0; // F was not within D. 
    } 
    G = dirname(G); 
} 

的samefile功能很簡單:

int samefile(dev_t ddev, ino_t dino, const char *path) { 
    struct stat st; 
    if (0 == stat(path, &st)) { 
    return ddev == st.st_dev && dino == st.st_no; 
    } else { 
    throw ...; // or return error value (but also change the caller to detect it) 
    } 
} 

這將在POSIX文件系統的工作。但是許多文件系統不是POSIX。需要注意的問題包括:

  1. 設備/ inode不唯一的文件系統。一些FUSE文件系統就是這樣的例子;當底層文件系統沒有它們時,它們有時會構成inode數字。他們不應該重新使用inode號碼,但是一些FUSE文件系統有錯誤。
  2. 破壞的NFS實現。在某些系統上,所有的NFS文件系統都有相同的設備編號。如果它們通過服務器上存在的inode號碼,這可能會導致問題(儘管我從來沒有在實踐中看到它發生過)。
  3. Linux綁定掛載點。如果/a/b的綁定掛載,則/a/1正確地看起來在/a內,但是對於上面的實現,/b/1也似乎在/a內。我認爲這可能是正確的答案。但是,如果這不是您喜歡的結果,則可以通過更改return 1大小寫以調用strcmp()來比較路徑名,從而輕鬆修復此問題。然而,爲了這個工作,您需要先在F和D上撥打realpath開始。realpath調用可能非常昂貴(因爲它可能需要多次點擊磁盤)。
  4. 特殊路徑//foo/bar。 POSIX允許以//開頭的路徑名稱是特殊的,這種方式有點不明確。實際上,我忘記了POSIX提供的關於語義的確切級別的保證。我認爲POSIX允許//foo/bar//baz/ugh引用同一個文件。設備/ inode檢查應該仍然適合您,但您可能會發現它不會(也就是說,您可能會發現//foo/bar//baz/ugh可以引用相同的文件,但具有不同的設備/ inode編號)。

這個回答假設我們開始與F和D.如果不能保證這一點,你可能需要做使用realpath()getcwd()一些轉換的絕對路徑。如果當前目錄的名稱長於PATH_MAX(這肯定會發生),這將會成爲問題。

相關問題