天天看點

Linux中的umask及權限管理

什麼是umask

Linux中,“Every thing is a file”, 談到檔案就涉及到檔案權限管理,umask(user file-creation mode mask)的作用是用來設定每一個新增的檔案、目錄的權限(permission)。

事實上,每個使用者在登入系統時,都會有預設的umask,比如檢視系統的/etc/bashrc檔案:

Linux中的umask及權限管理

一般使用者的umask預設是002, 其它特殊使用者如系統程序等的umask是022。

使用者也可以在自己的bashrc檔案中單獨設定自己的umask。

使用者umask和檔案/目錄的預設權限相作用,就決定了新增的檔案、目錄的權限。

檔案權限

在系統的bits/stat.h檔案中,定義了一系列的檔案權限标志(用八進制數表示):

Linux中的umask及權限管理

使用者(user)權限定義:

Linux中的umask及權限管理

組(group)權限定義:

Linux中的umask及權限管理

其它(others)權限定義:

Linux中的umask及權限管理

這樣,我們可以用一個八進制數表示讀/寫/執行權限如下:

  • 通過八進制的0400/0200/0100可以表示使用者自己的讀/寫/執行權限。簡單的,用一個八進制數可以表示這三種權限的各種組合:比如0700表示使用者自己對檔案同時具有讀/寫/執行權限;0600表示使用者自己對檔案同時具有讀/寫權限,但沒有執行權限;
  • 通過八進制的0040/0020/0010可以表示同組使用者的讀/寫/執行權限。用一個八進制數可以表示這三種權限的各種組合:比如0070表示同組使用者對檔案同時具有讀/寫/執行權限;0060表示同組使用者對檔案同時具有讀/寫權限,但沒有執行權限;
  • 通過八進制的0004/0002/0001可以表示其它使用者的讀/寫/執行權限。用一個八進制數可以表示這三種權限的各種組合:比如0007表示其它使用者對檔案同時具有讀/寫/執行權限;0006表示其它使用者對檔案同時具有讀/寫權限,但沒有執行權限;

将上面的三個八進制數通過或操作合并,就可以用一個八進制數來表示檔案權限:也就是一個八進制數的某一位分别表示使用者自己/同組使用者/其它使用者分别對該檔案的讀/寫/執行權限。比如用0765:表示使用者自己對檔案具有讀/寫/執行權限,同組使用者對檔案具有讀/寫權限,其它使用者對檔案具有讀/執行權限。

檔案預設權限

在bits/stat.h也定義了檔案的預設權限為0666,目錄的預設權限為0777:

Linux中的umask及權限管理

為什麼檔案的預設權限為0666,目錄的預設權限為0777呢?這是因為:

  • 建立檔案一般是用來讀寫,是以預設情況下所有使用者都具有讀寫權限,為了安全起見,沒有可執行權限,是以建立的檔案的預設權限為0666
  • 目錄的執行權限表示的是打開權限,是以這個權限必須要有,另外目錄建立後也需要讀寫,是以目錄的預設權限為0777。

使用者可以通過改變umask值來改變新建立的檔案/目錄的權限:使用者設定的umask值取反與檔案/目錄的預設權限求交就得到了建立檔案/目錄的權限:

~umask & 0666 // user file permission
~umask & 0777 // user directory permission
           

比如,如果使用者的umask是0002,那麼:

~0002 & 0666 = 0664 // user file permission
~0002 & 0777 = 0775 // user directory permission
           

這裡的計算就是八進制數的取反與求交操作,一個快速的心算擷取權限的辦法是用預設權限減去掩碼就得到了對應的檔案或目錄權限:比如0666-0002=0664,0777-0002=0775

mode_t權限表示

Linux系統程式設計中用mode_t來表示權限:mode_t的類型實際上是unsigned int:

Linux中的umask及權限管理

這裡有個程式可以比較友善的列印檔案權限的數字表示與其字元表示:

#include <sys/stat.h>
#include <stdbool.h>
#include <stdio.h>

enum class { CLASS_OWNER, CLASS_GROUP, CLASS_OTHER };
enum permission { PERMISSION_READ, PERMISSION_WRITE, PERMISSION_EXECUTE };
const mode_t EMPTY_MODE = 0;
mode_t perm(enum class c, enum permission p) { return 1 << ((3-p) + (2-c)*3); }
bool mode_contains(mode_t mode, enum class c, enum permission p) { return mode & perm(c, p); }
mode_t mode_add(mode_t mode, enum class c, enum permission p) { return mode | perm(c, p); }
mode_t mode_rm(mode_t mode, enum class c, enum permission p) { return mode & ~perm(c, p); }

// buf must have at least 10 bytes
void strmode(mode_t mode, char * buf) {
  const char chars[] = "rwxrwxrwx";
  for (size_t i = 0; i < 9; i++) {
    buf[i] = (mode & (1 << (8-i))) ? chars[i] : '-';
  }
  buf[9] = '\0';
}

int main(void) {
  char buf[10];
  mode_t examples[] = { 0, 0666, 0777, 0700, 0100, 01, 02, 03, 04, 05, 06, 07 };
  size_t num_examples = sizeof(examples) / sizeof(examples[0]);
  for (size_t i = 0; i < num_examples; i++) {
    strmode(examples[i], buf);
    printf("%04o is %s\n", examples[i], buf);
  }
  return 0;
}
           

輸出為:

0000 is ---------
0666 is rw-rw-rw-
0777 is rwxrwxrwx
0700 is rwx------
0100 is --x------
0001 is --------x
0002 is -------w-
0003 is -------wx
0004 is ------r--
0005 is ------r-x
0006 is ------rw-
0007 is ------rwx
           

chmod改變檔案權限

用chmod函數可以改變檔案或目錄權限,Bash中也可以用chmod改變權限,一般的做法是:

chmod 777 filename 
chmod u=rwx filename
chmod go=rx filename
           

特殊權限位的字元表示

當然,權限的字元表示不僅僅有rwx, 這裡列出了所有的字元表示:

Linux中的umask及權限管理

其中,s/S/t/T屬于特殊權限字元辨別,它們對應于SUID/SGID/SVTX:

Linux中的umask及權限管理

當使用者設定了SUID,SGID,SVTX,s/S/t/T字元辨別就會出現在執行權限x的位置,下面單獨介紹它們。

SUID

SUID叫做執行時使用者ID權限,如果設定了SUID,同時也設定了執行權限,本應出現x的位置就會用s表示:

$ ls a -l
-rw-rw-r-- 1 qsun qsun 2 May 19 16:15 a

$ chmod 4777 a 

$ ls a -l
-rwsrwxrwx 1 qsun qsun 2 May 19 16:15 a
           

但是如果給檔案設定了SUID, 卻沒有設定執行權限,那麼本應出現-字元的位置就會出現大寫的S:

$ ls -l a
-rw-rw-r-- 1 qsun qsun 0 Nov  1 10:05 a

$ chmod 4677 a

$ ls -l a
-rwSrwxrwx 1 qsun qsun 0 Nov  1 10:05 a
           

作用:當檔案所有者對一個可執行檔案設定了SUID位後,那麼任意對該檔案有執行權限的使用者在運作該檔案時,将會擁有檔案所有者的權限。

比如下面的例子:

#include <stdio.h>
int main() {
  FILE *fp = fopen("/home/qsun/suid-test.txt", "wt");
  if (fp) {
    fprintf(fp, "I'll be back\n");
    fclose(fp);
    printf("File created.\n");
  } else {
    printf("File not created.\n");
  }
  return 0;
}

           

編譯為可執行檔案後,如果不設定SUID位,那麼運作的結果将是“File not created.",如果設定了SUID位後,其它使用者也可以在我的home目錄建立檔案,盡管其沒有我的home目錄的寫權限,但因為SUID,其運作這個可執行檔案時,具有了我對home目錄的寫權限。

SGID

SGID叫做執行時組ID權限,SGID和SUID類似,用來特殊标定組使用者的執行權限:

$ chmod 2777 a

$ ls -l a
-rwxrwsrwx 1 qsun qsun 0 Nov  1 10:17 a

$ chmod 2767 a

$ ls -l a
-rwxrwSrwx 1 qsun qsun 0 Nov  1 10:17 a
           

作用: 當對一個目錄設定了SGID位後,那麼在這個目錄下建立的任何新的目錄和檔案将會繼承該目錄的group标志位;反之如果某個目錄沒有設定SGID位,那麼,在該目錄下建立的檔案和目錄将會屬于使用者的預設組,如下例子:

[ torvalds ~ ] $ groups torvalds
torvalds : torvalds engineers

[ torvalds ~ ] $ stat -c "%a %U:%G %n" ./music/
2770 root:engineers ./music/

[ torvalds ~ ] $ mkdir ~/music/electronic

[ torvalds ~ ] $ stat -c "%U:%G %n" ./music/electronic/
torvalds:engineers ./music/electronic/

[ torvalds ~ ] $ echo 'NEW FILE' > ~/music/imagine.txt

[ torvalds ~ ] $ stat -c "%U:%G %n" ./music/imagine.txt
torvalds:engineers ./music/imagine.txt

[ torvalds ~ ] $ touch ~/test

[ torvalds ~ ] $ stat -c "%U:%G %n" ~/test
torvalds:torvalds ~/test
           

SVTX

SVTX對應sticky bit,又稱位粘住位, 在Linux系統中,核心忽略對檔案設定的sticky bit,隻支援對目錄設定sticky bit, 和SUID與SGID類似,如果其它使用者擁有執行權限,設定了sticky bit後x對應的位置會變為t;如果其它使用者沒有執行權限,則變為T:

$ mkdir testfolder


$ ls -l
total 4
drwxrwxr-x 2 qsun qsun 4096 Nov  1 11:13 testfolder

[twdev2][5640][11:13:58] ~/f 
$ chmod 1777 testfolder

$ ls -l
total 4
drwxrwxrwt 2 qsun qsun 4096 Nov  1 11:13 testfolder

[twdev2][1][5643][11:14:29] ~/f 
$ chmod 1776 testfolder

[twdev2][5644][11:14:34] ~/f 
$ ls -l
total 4
drwxrwxrwT 2 qsun qsun 4096 Nov  1 11:13 testfolder

           

作用: 在Linux系統中,對目錄設定sticky bit後,那麼該目錄下的檔案和目錄将不會被除了檔案所有者或root使用者外的其它使用者重命名或者删除。如果不設定sticky bit,所有對該目錄擁有寫和執行權限的使用者都可以删除或者重命名該目錄下的檔案。Linux 系統中的/tmp目錄的權限設定就是這樣一個使用了sticky bit的範例:

drwxrwxrwt.  387 root                    root  45056 Nov  9 21:24 tmp
           

Reference:

  1. https://en.wikipedia.org/wiki/Setuid
  2. https://wiki.archlinux.org/index.php/File_permissions_and_attributes

繼續閱讀