Linux下ls命令顯示符號鏈接權限為777的探索


Linux下ls命令顯示符號鏈接權限為777的探索

                                               ——深入ls、鏈接、文件系統與權限         

                                                                                                       by Ascii0x03,http://www.cnblogs.com/ascii0x03/p/6442420.html

一、摘要

ls是Linux和Unix下最常使用的命令之一,主要用來列舉目錄下的文件信息,-l參數允許查看當前目錄下所有可見文件的詳細屬性,包括文件屬性、所有者、文件大小等信息。但是,當其顯示符號鏈接的屬性時,無論其指向文件屬性如何,都會顯示777,即任何人可讀可寫可執行。本文從ls命令源碼出發,由淺入深地分析該現象的原因,簡略探究了Linux 4.10下的符號鏈接鏈接、文件系統與權限的源碼實現。

 

關鍵詞:Linux ls 符號鏈接 文件系統 權限 源碼分析

二、引言

2.1 Linux文件權限

在Linux中每個文件有所有者、所在組、其它組的概念[11]。所有者一般為文件的創建者,誰創建了該文件,就天然的成為該文件的所有者;當某個用戶創建了一個文件后,這個文件的所在組就是該用戶所在的組;除開文件的所有者和所在組的用戶外,系統的其它用戶都是文件的其它組。ls 命令將每個由 Directory 參數指定的目錄或者每個由 File 參數指定的名稱寫到標准輸出,以及所要求的和標志一起的其它信息。ls -l中顯示的內容常如下所示:

-rwxrw-r‐-1 root root 1213 Feb 2 09:39 abc

前10個字符說明了文件類型與權限。第一個字符代表文件(-)、目錄(d),鏈接(l),其余字符每3個一組(rwx),讀(r)、寫(w)、執行(x)。第一組rwx:文件所有者的權限是讀、寫和執行;第二組rw-:與文件所有者同一組的用戶的權限是讀、寫但不能執行;第三組r--:不與文件所有者同組的其他用戶的權限是讀不能寫和執行。權限也可用數字表示為:r=4,w=2,x=1  因此rwx=4+2+1=7。

2.2 符號鏈接

如前所述,若第一個字符顯示為l,說明該文件是符號鏈接。符號鏈接(軟鏈接)是一類特殊的文件, 其包含有一條以絕對路徑或者相對路徑的形式指向其它文件或者目錄的引用[12]。符號鏈接的操作是透明的:對符號鏈接文件進行讀寫的程序會表現得直接對目標文件進行操作。某些需要特別處理符號鏈接的程序(如備份程序)可能會識別並直接對其進行操作。一個符號鏈接文件僅包含有一個文本字符串,其被操作系統解釋為一條指向另一個文件或者目錄的路徑。它是一個獨立文件,其存在並不依賴於目標文件。如果刪除一個符號鏈接,它指向的目標文件不受影響。如果目標文件被移動、重命名或者刪除,任何指向它的符號鏈接仍然存在,但是它們將會指向一個不復存在的文件。這種情況被有時被稱為被遺棄。

但是,我們常常發現,創建符號鏈接其權限就會顯示為lrwxrwxrwx,為什么?是ls命令對符號鏈接進行了處理,還是文件本身權限即如此?這樣會不會帶來一些安全隱患?懷着這些問題,本文由淺入深,從ls命令出發,探索了其背后的系統調用至vfs文件系統實現細節,力求解釋這些問題。但作者水平有限,尚有很多細節不清楚,不對之處懇請批評指正。

 

 

 

 

三、ls命令分析

ls命令是Linux shell下最常用的命令之一,主要用來列舉目錄下的文件信息。經過搜索引擎查找[1],要查看該命令的源代碼需要下載對應軟件包coreutils的源代碼。其實只要知道了軟件包的名字,既可以按照文獻[1]的方法使用apt-get source下載,也可以從軟件包coreutils的官網[2]下載。下載完畢后,使用source insight軟件建立工程,即可方便地開始源碼閱讀。本文使用截止2017年2月18日最新版本的coreutils-8.26。

打開/src/ls.c,從main函數開始,忽略開始的初始化、顏色設定等內容,1451行調用的decode_switches對參數進行了一些處理,由於研究的是ls程序,所以第一個switch(ls_mode)關注LS_LS。接下來,1703行設置dereference = DEREF_UNDEFINED。關鍵部分代碼為1752行的一個switch(true),它根據傳入的參數,設置相應的標志,如-l設置format = long_format,H、I、L設置了dereference的一些模式,由於作者平時經常使用的是ls –l,所以僅關注-l選項下的情況,dereference仍然為DEREF_UNDEFINED。同時,該函數2125行對format == long_format的情況,做了一些格式輸出上的工作。

 

 

發現1467行對dereference變量的判斷影響了如何處理符號鏈接。若僅使用-l選項,dereference賦值為DEREF_NEVER,即僅僅拷貝復制符號鏈接自身。

 

若設置了遞歸枚舉,設置一個哈希表來檢測是否出現了目錄環。接下來開辟cwd_file變量空間,其是指向fileinfo結構體的向量指針,保存了要描述的文件。關於fileinfo結構體源碼中已經給了很好的注釋,其中struct stat類型的變量stat具體描述了文件的信息,往往由stat()或lstat()函數返回。struct stat類型的定義可以在Linux源碼include\uapi\asm-generic中找到,可以看出新版本64位中與常見文檔中相比增加了許多pad填充,並將類型的一些宏定義取消了,直接采用了unsigned long。

 

下面主要調用了gobble_file函數,添加文件到當前的文件表中,即放到cwd_file中未使用的第一個位置上。3131行的switch語句根據dereference值,調用stat()或lstat()函數,由上面分析可知,ls –l是DEREF_NEVER,故調用lstat()函數,並將結果存入fileinfo.stat中(代碼中是變量f);接下來,代碼再根據結構體fileinfo.stat,對fileinfo其他部分賦值,在long_format情況下,fileinfo. scontext是SELinux有關函數lgetfilecon()獲得的安全上下文(context)。同時也可以看出,默認-l選項是不對符號鏈接進行追蹤的,所以調用的函數也都是對應版本。3235行判斷當文件為符號鏈接且模式為long_format時會成立,由此賦值了fileinfo的linkok、linkmode等值。接下來函數主要根據要輸出哪些信息,將fileinfo中的值保存了下來。

最后,在main函數1538行,根據文件的數目,調用print_current_files ()來輸出文件內容,print_long_format()中第3967行通過filemodestring()函數將文件的讀寫執行權限填入了modebuf,該函數在filemode.c中定義。在填入時,ls程序未對符號鏈接做特殊處理,由此可見,符號鏈接權限問題的關鍵在於lstat()函數的實現是如何填入stat結構體中st_mode的。

四、lstat系統調用

4.0 系統調用與文件系統基礎

4.0.1 從C語言到系統調用

stat是用來獲得文件信息的系統調用[3],要尋找該系統調用的源代碼,首先要理解系統調用的流程。這里參考了文獻[4][5],根據自己理解,C語言調用stat函數時,調用的是C庫對該函數的實現,接着執行庫中函數的具體實現的代碼,其中非常關鍵的一句代碼就是 int 0x80,中斷使得進程從用戶態切換到內核態,中斷處理程序然后開始執行內核中對應80號中斷的系統調用處理程序的代碼 system_call;system_call 系統調用處理程序就根據傳入的系統調用號從系統調用服務程序數組中尋找對應系統調用服務程序,最后執行完成后按照調用順序的相反順序一步步返回結果。

 

 

根據一般規律,系統調用定義的名字就是在函數前面加一個sys_,由此在include\linux\syscalls.h中發現了一系列stat的聲明,而fs\Stat.c中是對應的定義。(內核中使用SYSCALL_DEFINE2的宏定義來定義系統調用,展開就是聲明的形式。)這里會發現,4.10內核中同時存在newstat與stat,無論新舊,實現都是使用了vfs_stat函數,傳入參數為kstat,差別在於宏倒數第二個參數的類型。(但是這個參數具體有什么作用?實現中好像並沒有用到這個參數。)

 

接下來需要看vfs_lstat的實現,他與vfs_stat都是調用了vfs_fstatat,區別在於vfs_lstat給最后一個參數賦值為了AT_SYMLINK_NOFOLLOW,說明不要追蹤符號鏈接。

vfs_fstatat首先對flag參數進行檢查,必須有(並非等於)規定的幾種標識之一;然后調用user_path_at,根據返回結果再調用vfs_getattr和path_put;之后看似是一個錯誤處理,會返回到retry,沒有錯誤則函數退出返回。對關鍵數據stat賦值的部分應該就在vfs_getattr函數了。

4.0.2 文件系統基礎

為了進行后面的分析,這里需要Linux內核文件系統有一定的了解[6][7]。Linux 有着極其豐富的文件系統,大體上可分如下幾類:

網絡文件系統,如 nfs、cifs 等;

磁盤文件系統,如 ext4、ext3 等;

特殊文件系統,如 proc、sysfs、ramfs、tmpfs 等。

實現以上這些文件系統並在 Linux 下共存的基礎就是 Linux VFS(Virtual File System 又稱 Virtual Filesystem Switch),即虛擬文件系統。VFS 作為一個通用的文件系統,抽象了文件系統的四個基本概念:文件、目錄項 (dentry)、索引節點 (inode) 及掛載點,其在內核中為用戶空間層的文件系統提供了相關的接口。VFS 實現了 open()、read() 、stat()等系統調並使得 cp 等用戶空間程序可跨文件系統。VFS 真正實現了上述內容中:在 Linux 中除進程之外一切皆是文件。

 

Linux VFS 存在四個基本對象:超級塊對象 (superblock object)、索引節點對象 (inode object)、目錄項對象 (dentry object) 及文件對象 (file object)。超級塊對象代表一個已安裝的文件系統;索引節點對象代表一個文件;目錄項對象代表一個目錄項,如設備文件 event5 在路徑 /dev/input/event5 中,其存在四個目錄項對象:/ 、dev/ 、input/ 及 event5。文件對象代表由進程打開的文件。這四個對象與進程及磁盤文件間的關系如圖,其中 d_inode 即為硬鏈接。為文件路徑的快速解析,Linux VFS 設計了目錄項緩存(Directory Entry Cache,即 dcache)。下面列出幾個關鍵數據結構,並在關注的部分給出注釋。

 

  1 struct nameidata { //文件查找臨時結構體
  2 
  3 struct path path;//包含vfsmount掛載點和dentry目錄項
  4 
  5 struct qstr last;
  6 
  7 struct path root;
  8 
  9 struct inode       *inode; /* path.dentry.d_inode */
 10 
 11 unsigned int      flags;
 12 
 13 unsigned   seq, m_seq;
 14 
 15 int             last_type; /*路徑中的最后一個component的類型*/
 16 
 17 unsigned depth; //符號鏈接嵌套的級別
 18 
 19 int             total_link_count;
 20 
 21 struct saved {
 22 
 23           struct path link;
 24 
 25           struct delayed_call done;
 26 
 27           const char *name;
 28 
 29           unsigned seq;
 30 
 31 } *stack, internal[EMBEDDED_LEVELS];
 32 
 33 struct filename   *name; //保存要查找的文件名
 34 
 35 struct nameidata *saved;
 36 
 37 struct inode       *link_inode;
 38 
 39 unsigned   root_seq;
 40 
 41 int             dfd;
 42 
 43 };
 44 
 45 struct dentry {//目錄項對象
 46 
 47 /* RCU lookup touched fields */
 48 
 49 unsigned int d_flags;           /* protected by d_lock */
 50 
 51 seqcount_t d_seq;               /* per dentry seqlock */
 52 
 53 struct hlist_bl_node d_hash;        /* lookup hash list */
 54 
 55 struct dentry *d_parent;      /* parent directory */
 56 
 57 struct qstr d_name;
 58 
 59 struct inode *d_inode;                 /* Where the name belongs to - NULL is
 60 
 61                                     * negative */
 62 
 63 unsigned char d_iname[DNAME_INLINE_LEN];     /* small names */
 64 
 65  
 66 
 67 /* Ref lookup also touches following */
 68 
 69 struct lockref d_lockref;       /* per-dentry lock and refcount */
 70 
 71 const struct dentry_operations *d_op;//目錄項方法
 72 
 73 struct super_block *d_sb;    /* The root of the dentry tree */
 74 
 75 unsigned long d_time;                /* used by d_revalidate */
 76 
 77 void *d_fsdata;                   /* fs-specific data */
 78 
 79  
 80 
 81 union {
 82 
 83           struct list_head d_lru;          /* LRU list */
 84 
 85           wait_queue_head_t *d_wait;        /* in-lookup ones only */
 86 
 87 };
 88 
 89 struct list_head d_child;      /* child of parent list */
 90 
 91 struct list_head d_subdirs;   /* our children */
 92 
 93 /*
 94 
 95  * d_alias and d_rcu can share memory
 96 
 97  */
 98 
 99 union {
100 
101           struct hlist_node d_alias;     /* inode alias list */
102 
103           struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
104 
105         struct rcu_head d_rcu;
106 
107 } d_u;
108 
109 };
110 
111 struct inode { //索引節點
112 
113 umode_t                    i_mode; //文件類型與訪問權限,是本文所關注的重點部分
114 
115 unsigned short           i_opflags;//2.6內核中沒有的字段,哪里去找這個字段注釋?
116 
117 kuid_t                        i_uid;
118 
119 kgid_t                        i_gid;
120 
121 unsigned int              i_flags;
122 
123  
124 
125 #ifdef CONFIG_FS_POSIX_ACL
126 
127 struct posix_acl  *i_acl;
128 
129 struct posix_acl  *i_default_acl;
130 
131 #endif
132 
133  
134 
135 const struct inode_operations     *i_op; //索引節點的操作
136 
137 struct super_block      *i_sb;
138 
139 struct address_space  *i_mapping;
140 
141  
142 
143 #ifdef CONFIG_SECURITY
144 
145 void                   *i_security;
146 
147 #endif
148 
149  
150 
151 /* Stat data, not accessed from path walking */
152 
153 unsigned long            i_ino;
154 
155 /*
156 
157  * Filesystems may only read i_nlink directly.  They shall use the
158 
159  * following functions for modification:
160 
161  *
162 
163  *    (set|clear|inc|drop)_nlink
164 
165  *    inode_(inc|dec)_link_count
166 
167  */
168 
169 union {
170 
171           const unsigned int i_nlink;
172 
173           unsigned int __i_nlink;
174 
175 };
176 
177 dev_t                 i_rdev;
178 
179 loff_t                  i_size;
180 
181 struct timespec          i_atime;
182 
183 struct timespec          i_mtime;
184 
185 struct timespec          i_ctime;
186 
187 spinlock_t          i_lock;       /* i_blocks, i_bytes, maybe i_size */
188 
189 unsigned short          i_bytes;
190 
191 unsigned int              i_blkbits;
192 
193 blkcnt_t             i_blocks;
194 
195  
196 
197 #ifdef __NEED_I_SIZE_ORDERED
198 
199 seqcount_t                 i_size_seqcount;
200 
201 #endif
202 
203  
204 
205 /* Misc */
206 
207 unsigned long            i_state;
208 
209 struct rw_semaphore  i_rwsem;
210 
211  
212 
213 unsigned long            dirtied_when;     /* jiffies of first dirtying */
214 
215 unsigned long            dirtied_time_when;
216 
217  
218 
219 struct hlist_node        i_hash;
220 
221 struct list_head  i_io_list;     /* backing dev IO list */
222 
223 #ifdef CONFIG_CGROUP_WRITEBACK
224 
225 struct bdi_writeback   *i_wb;                /* the associated cgroup wb */
226 
227  
228 
229 /* foreign inode detection, see wbc_detach_inode() */
230 
231 int                     i_wb_frn_winner;
232 
233 u16                   i_wb_frn_avg_time;
234 
235 u16                   i_wb_frn_history;
236 
237 #endif
238 
239 struct list_head  i_lru;          /* inode LRU list */
240 
241 struct list_head  i_sb_list;
242 
243 struct list_head  i_wb_list;   /* backing dev writeback list */
244 
245 union {
246 
247           struct hlist_head i_dentry;
248 
249           struct rcu_head          i_rcu;
250 
251 };
252 
253 u64                   i_version;
254 
255 atomic_t            i_count;
256 
257 atomic_t            i_dio_count;
258 
259 atomic_t            i_writecount;
260 
261 #ifdef CONFIG_IMA
262 
263 atomic_t            i_readcount; /* struct files open RO */
264 
265 #endif
266 
267 const struct file_operations *i_fop;       /* former ->i_op->default_file_ops */
268 
269 struct file_lock_context       *i_flctx;
270 
271 struct address_space  i_data;
272 
273 struct list_head  i_devices;
274 
275 union {
276 
277           struct pipe_inode_info        *i_pipe;
278 
279           struct block_device     *i_bdev;
280 
281           struct cdev                 *i_cdev;
282 
283           char                   *i_link;
284 
285           unsigned           i_dir_seq;
286 
287 };
288 
289  
290 
291 __u32                        i_generation;
292 
293  
294 
295 #ifdef CONFIG_FSNOTIFY
296 
297 __u32                        i_fsnotify_mask; /* all events this inode cares about */
298 
299 struct hlist_head i_fsnotify_marks;
300 
301 #endif
302 
303  
304 
305 #if IS_ENABLED(CONFIG_FS_ENCRYPTION)
306 
307 struct fscrypt_info       *i_crypt_info;
308 
309 #endif
310 
311  
312 
313 void                   *i_private; /* fs or device private pointer */
314 
315 };
316 
317  
318 
319 struct file { //文件對象,描述進程怎樣與一個打開的文件進行交互
320 
321 union {
322 
323           struct llist_node fu_llist;
324 
325           struct rcu_head         fu_rcuhead;
326 
327 } f_u;
328 
329 struct path         f_path;
330 
331 struct inode               *f_inode;   /* cached value */
332 
333 const struct file_operations *f_op;
334 
335  
336 
337 /*
338 
339  * Protects f_ep_links, f_flags.
340 
341  * Must not be taken from IRQ context.
342 
343  */
344 
345 spinlock_t          f_lock;
346 
347 atomic_long_t            f_count;
348 
349 unsigned int              f_flags;
350 
351 fmode_t                     f_mode;
352 
353 struct mutex              f_pos_lock;
354 
355 loff_t                  f_pos; //文件偏移
356 
357 struct fown_struct      f_owner;
358 
359 const struct cred        *f_cred; //進程相關安全上下文信息,如uid、權限等
360 
361 struct file_ra_state      f_ra;
362 
363  
364 
365 u64                   f_version;
366 
367 #ifdef CONFIG_SECURITY
368 
369 void                   *f_security;
370 
371 #endif
372 
373 /* needed for tty driver, and maybe others */
374 
375 void                   *private_data;
376 
377  
378 
379 #ifdef CONFIG_EPOLL
380 
381 /* Used by fs/eventpoll.c to link all the hooks to this file */
382 
383 struct list_head  f_ep_links;
384 
385 struct list_head  f_tfile_llink;
386 
387 #endif /* #ifdef CONFIG_EPOLL */
388 
389 struct address_space  *f_mapping;
390 
391 } __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */
View Code

 

4.1 user_path_at函數

有了基礎補充后,繼續4.0.1節分析系統調用。user_path_at調用並返回user_path_at_empty的返回值,user_path_at_empty 返回filename_lookup的返回值,傳參數時使用了getname_flags函數(實在看不懂這個函數,但是2.6內核要簡潔的多,直接返回char*,4.10多設計了一個filename *)將用戶傳入的文件名const char __user *name轉化為了struct filename *類型,其中__user宏定義為空的,可能是為了標記該參數由用戶傳入吧。(為什么要一層一層來調用,不怕效率低嗎?)

 

 

filename_lookup主要調用了path_lookupat函數,在之前先調用了set_nameidata,對nameidata進行了一些初始化賦值,將要查詢的文件名放入了結構體nameidata中,並且將從current取出的nameidata保存下來,組成了一個鏈表。(這里發現current是宏定義,開始的時候source insight自動追蹤變成了循環宏定義,應該是追蹤錯了頭文件,因為有許多個current.h。后來查閱資料看是獲得調用系統調用進程的數據結構信息。)

 

 

 

 

4.1.1 path_lookupat函數

接着進入path_lookupat函數,此時參數為0 | LOOKUP_RCU,即LOOKUP_ RCU。RCU(Read-Copy Update)是內核數據同步的一種鎖機制。

1.函數首先調用path_init()函數,path_init()函數主要是初始化查詢,將nd實例的mnt和dentry成員設置為根目錄或者工作目錄的對應項

a,絕對路徑(以/開始),獲得根目錄的dentry。它存儲在task_struct中fs指向的fs_struct結構中。task_struct->fs_struct.root 。

b,相對路徑,直接從當前進程task_struct結構中的獲得指針fs,它指向的一個fs_struct,fs_struct中有一個指向“當前工作目錄”的dentry。

 2,path_lookupat()然后循環調用link_path_walk()函數。link_path_walk()函數將傳入的路徑名轉化為dentry目錄項:

首先跳過路徑名的’/’,如果只有'/'則直接返回0;得到正確的路徑名后,進入一個循環,每次都調用may_lookup()函數對inode節點做權限檢查,如果權限不夠也直接返回fail,在Unix中,只有目錄是可執行的,它才可以被遍歷;接下來計算的哈希與目錄項高速緩存有關;該循環不斷更新last_type和last,如果是最后一部分的返回,若不是則調用walk_component()函數。walk_component先處理LAST_DOTS,若發現LAST_NORM類型,即普通文件,則調用lookup_fast()在緩存中查找,若沒有好的結果則調用lookup_slow(),獲得i_mutex,重新檢查緩存並向文件系統查找,他們都會調用follow_managed來處理掛載點;若有必要,期間walk_component會更新nameidata結構體的path。

link_path_walk會隨着文件路徑每部分深入,並追蹤其間遇到的每個符號鏈接,直到其到達最后一部分,返回給nameidata.last。這個link_path_walk()函數本身非常復雜,也比較難懂,細節內容可參考文獻[8][9][13],[13]對源碼的注釋非常清楚。link_path_walk函數主要是根據給定的路徑,找到最后一個路徑分量的目錄項對象和安裝點。

 3. 在循環中還會調用trailing_symlink()函數來繼續追蹤最后部分的符號鏈接。trailing_symlink()會先調用may_follow_link(),這個函數檢查符號鏈接的一些不安全權限情況。接着調用get_link(),先更新相關的訪問時間等信息,然后調用inode中的get_link()方法完成符號鏈接解析;注意,原來的follow_link被get_link代替,而put_link,通過在get_link中設置set_delayed_call代替。

4. 最后對nd進行一些恢復收尾工作。

4.1.2 收尾

audit_inode調用了__audit_inode,定義在kernel/auditsc.c中,保存查找的inode和device,從名字猜測是審計用,這里先不關心。然后恢復current->nameidata,並釋放name內存,與開頭對應。這樣filename_lookup結束,返回retval變量至user_path_at函數,即返回path_lookupat的結果。由此可見user_path_at就是檢查是否存在這個文件,以及相關權限是否允許。

4.2 vfs_getattr函數

4.2.1 security_inode_getattr

security_inode_getattr定義在security/security.c,首先檢查dentry的inode的i_flags是否為S_PRIVATE,即inode文件系統的安裝標識。若不是,則調用call_int_hook(inode_getattr, 0, path),這個宏定義就看不懂了,涉及到security_hook_heads的很多東西,這里先忽略。

4.2.2 vfs_getattr_nosec

注釋說明是在沒有安全檢查的情況下獲得屬性,即沒有調用security_inode_getattr,實現很簡單,從dentry目錄項中獲得inode,然后調用inode索引節點對象的getattr方法,再用generic_fillattr填充到返回的stat中,stat->mode=inode->i_mode。Inode方法中保存的函數指針,就指向了每個具體文件操作系統的的函數。

 

五、結論

通過分析,ls –l獲得的符號鏈接就是vfs下層文件系統getattr返回的信息,那么下層文件系統getattr如何實現?這取決於不同的具體文件系統,《深入理解Linux內核》中提到ext2使用generic_getattr,但是在4.10源碼中已經難以尋找到了,再深入的內容需要額外的耐心。那么如何查看inode的信息呢?Debugfs是一種特殊的文件系統,提供了把內核信息傳遞到用戶空間的方式,與/proc類似。在debugfs中執行mi命令+要查看的文件,可以得到完整的inode信息。如下圖所示,符號鏈接inode中的mode值確實為0120777。這會不會有什么安全隱患,為什么要這樣設計?

 

 

文獻[10]也有這樣的描述“Symbolic links (sometimes called soft links) do not link to inodes, but create a name to name mapping. Symbolic links are created with ln -s. As you can see below, the symbolic link gets an inode of its own. Permissions on a symbolic link have no meaning, since the permissions of the target apply. Hard links are limited to their own partition (because they point to an inode), symbolic links can link anywhere (other file systems, even networked).”大意為“符號鏈接不鏈接到inode結點,而是創建名字到名字的映射。符號鏈接擁有自己的inode結點,其權限是沒有意義的,因為應用的是鏈接目標文件的權限。符號鏈接可以鏈接至任何地方,如跨文件系統,甚至網絡”。在path_lookupat查找路徑時,已經對目錄進行了權限檢查,一般情況,如open系統調用,在路徑尋找的時候都會使用do_follow_link函數來自動解析掉符號鏈接,(之前分析的path_lookupat循環中trailing_symlink也是用來追蹤最后一個分量dentry為符號鏈接的情況,但應該由於設置了falg沒有繼續追蹤符號鏈接。奇怪的是根據flag沒有發現可以不調用trailing_symlink。)所以符號鏈接本身的權限沒有意義;對文件系統詳細分析的優秀文檔可見[14]。

但是,在分析源碼過程中見到的may_follow_link()函數就考慮了一些可能的安全隱患。此外,假想一種場景,若系統或A app想使用B app的憑證文件key,惡意的B app可以將key這個文件符號鏈接至其他任何地方,如C app的憑證,會不會引發這樣的一些問題?仍待探索。

 

 

六、反思

1. 想看懂Linux內核源碼,甚至只是filesystem這一部分的源碼,都需要對整個Linux內核源碼有一定的認識。如遇到的系統調用,進程相關current,安全相關security.c的內容。

2. 看源碼前一定盡力找文檔!看源碼前一定盡力找文檔!看源碼前一定盡力找文檔!開始作者僅根據《深入理解Linux內核》這本書在探索path_lookupat,發現4.10與書上的2.6內核有不少的差別,所以一定要參考源碼目錄下的document目錄下的文檔(沒有國人翻譯成中文嗎?),里面講了源碼的實現細節,並且和現役版本對應。另外也可以搜索其他人分析源碼的博客,但往往比較舊。

3. 不要完全相信source insight的自動查找,遇見問題要相信自己的眼睛,再結合網上的Linux源碼索引,優先用google不要用百度。

4. 使用source insight建立工程太大的話同步符號要好久,所以不妨先只加入自己關注的部分,比如只加入fs文件夾下的源碼,這樣效率比較高。

5. 對內核中使用的數據結構和設計思路越清楚,越有利於看懂源碼。

6. 有了一定基礎后,看懂源碼不是夢,但非常非常需要耐心與時間,尤其是細節部分。由於自己最近耐心不佳,基礎也欠缺,所以沒有在意太多細節,僅力求了解全貌。

參考文獻

[1] Linux命令源碼的查看. http://blog.chinaunix.net/uid-27177626-id-3389673.html

[2] Coreutils - GNU core utilities. http://www.gnu.org/software/coreutils/coreutils.html

[3] stat (C System Call). http://codewiki.wikidot.com/c:system-calls:stat

[4] Linux 系統調用內核源碼分析. http://woshijpf.github.io/2016/05/10/Linux-%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/

[5] Linux系統調用(syscall)原理. http://gityuan.com/2016/05/21/syscall/

[6] 理解 Linux 的硬鏈接與軟鏈接. https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#major4

[7] Linux 的虛擬文件系統--各結構之間的聯系. http://scudong.blogbus.com/logs/11599350.html

[8] link_path_walk()路徑名查找.http://blog.chinaunix.net/uid-12567959-id-160996.html

[9] 《深入理解Linux內核》P495-P504

[10] Chapter 9. file links. http://linux-training.be/security/ch09.html#idp65315008

[11] Linux下用戶組、文件權限詳解. http://www.cnblogs.com/123-/p/4189072.html

[12] 符號鏈接. https://zh.wikipedia.org/zh/%E7%AC%A6%E5%8F%B7%E9%93%BE%E6%8E%A5

[13] Linux文件系統(七)---系統調用之open操作(三) 之 open_namei函數. http://blog.csdn.net/shanshanpt/article/details/39927553

[14] linux內核follow_link分析. http://blog.csdn.net/sanwenyublog/article/details/50856837


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM