1. 程式人生 > >第8章 使用者和組

第8章 使用者和組

每個使用者都有唯一的 登入名 和相關的數值型 使用者識別符號(UID) 。使用者可以屬於一個或多個 組(group)。每個組都有唯一 名稱組識別符號(GID)
UID和GID的主要目的是決定各種系統資源的所有權和控制程序訪問這些資源的許可權。例如,每個檔案都屬於一個特定的使用者和組,每個程序都有一些UIDs和GIDs,決定了程序的所有者是誰,訪問檔案是有哪些許可權(詳情請看第9章)。
在本章中,我們介紹系統中用於定義使用者和組的系統檔案,然後闡述用於從這些檔案獲取資訊的庫函式。最後,討論用於加密和認證登入密碼的 crypt() 函式。

8.1 The Password File: /etc/passwd

系統密碼檔案(password file)/etc/passwd 中的每行都表示系統中的一個使用者賬號。每行都有七個欄位組成,以冒號(:)分隔,如下:
在這裡插入圖片描述
這些欄位按順序分別是:

  1. 登入名(Login name) :這是一個具有唯一性的名稱,使用者登入時必須輸入該名稱。通常,也稱之為 使用者名稱(username)。我們也可以把登入名看成是一個可讀的識別符號,對應於數值型的使用者識別符號(userID,UID)。

  2. 加密密碼(Encrypted password) :該欄位是13個字元的加密密碼,會在8.5節更詳細地介紹。如果password欄位包含其他的字串(特別是,不是13個字元的字串),那麼就無法登入這個帳號,因為這個字串不能表示一個有效的加密密碼。值得注意的是,如果開啟了shadow password,那麼這個欄位會被忽略。在這種情況下,/etc/passwd中的password欄位按照慣例會是字母x

    (也可以出現任何非空字元),而加密過的密碼會儲存到shadow密碼檔案中(8.2節)。如果/etc/passwd中的password欄位是空的,那麼登入這個帳號就不需要密碼(即使開啟shadow password,也是這樣)。

    在這裡,我們假設使用了資料加密標準(Data Encryption Standard,DES)的加密方式。由於歷史原因,UNIX password-encryption 模式仍舊被廣泛使用。可以使用其他模式替換DES,例如MD5,根據輸入它會產生128位的資訊摘要(一種hash)。這個值會以34個字元的字串儲存 在密碼檔案(或者shadow密碼檔案)中。

  3. User ID(UID)

    :這是使用者的數值型的ID。如果這個欄位的值是0,那麼這個帳號具有超級使用者許可權。一般擁有這個帳號的登入名是root。在Linux2.2及更早的版本中,user ID是一個16位的值,允許使用0到65535範圍的數字。在Linux2.4及之後的版本中,使用32位儲存,可以使用更大的範圍。

    在密碼檔案中,同個user ID具有多條記錄是可以的(但是不常見),這樣同個user ID就可以使用多個登入名,允許多個使用者使用不同的密碼訪問相同的資源(例如檔案)。不同的登入名可以關聯不同的group IDs。

  4. Group ID (GID): 這是使用者第一個組的group ID,它是一個數值型ID。該使用者與更多組之間的關係在組檔案(group file)中定義。

  5. 註釋(comment):該欄位持有關於這個使用者的文字。該文字可以通過多種程式顯示,例如finger(1)

  6. Home目錄:這是使用者登入時所處的初始目錄。該欄位會成為HOME環境變數的值。

  7. Login shell:使用者登入時,傳遞哪些控制的程式(This is the program to which control is transferred once the user islogged in)。一般,這只是其中一個shell,例如bash,但是它可以是任何程式。如果欄位為空,那麼login shell預設是/bin/sh(the Bourne shell)。該欄位會成為SHELL環境變數的值。
    在獨立的系統中,所有的密碼相關資訊都存在檔案/etc/passwd中。但是我們也可以通過使用網路資訊系統(Network Information System, NIS)或輕量級目錄訪問協議(LDAP)將部分或全部密碼資訊傳送並儲存到遠端系統。只要程式使用隨後章節介紹的函式(getpwnam()、getpwuid()等)來訪問密碼資訊,那麼NIS或LDAP的使用對於應用來說是透明的。下面介紹的shadow密碼檔案和組檔案,也可以採用類似的方法。

8.2 The Shadow Password File: /etc/shadow

有史以來,UNIX系統在 /etc/passwd 密碼檔案中維護所有的使用者資訊,包括加密的密碼。這存在一個安全問題。因為很多非特權系統工具需要讀取密碼檔案中的其他資訊,所以所有使用者對密碼檔案都具有可讀許可權。密碼破解程式可以通過對大量可能的密碼進行加密的方式檢視這些加密後的密碼能否匹配上使用者加密過的密碼。shadow密碼檔案 /etc/shadow 是防止此類攻擊的方法。這樣,所有非敏感的使用者資訊儲存在公開可讀的密碼檔案中,而加密過的密碼儲存在shadow密碼檔案中,shadow密碼檔案只有對特權程式才是可讀的。
在這裡插入圖片描述
除了登入名(用於匹配密碼檔案中的相應記錄)和加密密碼,shadow密碼檔案中還包含了其他的一些與安全相關的欄位。關於這些欄位的更多詳情可以參考 shadow(5) 手冊頁。我們主要關心加密的密碼欄位,在8.5節講到crypt()函式時,會對該欄位進行更深入地探討。

8.3 The Group File: /etc/group

出於各種管理的目的,尤其是控制對檔案和其他資源的訪問,所以對使用者分組是很有用的。
使用者所屬組的集合(使用者屬於哪些組)需要通過密碼檔案的group ID欄位和組檔案中列出的使用者相結合之後才能得到。這種奇怪的跨兩個檔案的資訊分隔是有歷史原因的。在早期的UNIX實現中,使用者只能是一個組的成員。使用者初始組在登入時就已經由密碼檔案的group ID欄位決定了,然後可以通過使用 newgrp(1) 命令,提供 組密碼(group password) (如果組是受密碼保護的) 來改變所屬的組。4.2BSD引入了 多個同時組成員關係(multiple simultaneous group memberships) 的概念,這個概念隨後成為POSIX.1-1990的標準。在這種模式下,組檔案中列出了每個使用者額外的所屬組。(groups(1)命令顯示該shell程序所屬的組,如果將一個或多個使用者名稱作為命令列引數,那麼顯示這些使用者的所屬組)
組檔案 /etc/group 中的每一行表示一個組,每行包含由冒號(:)分割的四個欄位,如下所示:
在這裡插入圖片描述

這些欄位按順序分別是:

  1. 組名(group name):這是組的名稱。就像密碼檔案中的登入名,我們可以把它認為是可讀的識別符號,對應於數值型的組識別符號。
  2. 加密密碼(Encrypted password):該欄位表示組密碼(可不設定)。由於multiple group memberships的出現,組密碼在UNIX系統中現在已經很少用了。然而還是可以為組設定組密碼(特權使用者可以使用 passwd命令 來設定)。如果開啟了shadow密碼,那麼這個欄位被忽略(這種情況下,這個欄位一般是字元x,但是任何字串或者空字串都可以),而加密過的密碼被儲存在shadow組檔案中 /etc/gshadow, 該檔案只允許特權使用者和程式訪問。組密碼的加密方式與使用者密碼的加密方式類似(8.5節)。
  3. 組識別符號 (Group ID,GID) :這是組的數值型ID。通常有一個組的group ID是0,名稱為root。在Linux2.2及更早的版本中,group IDs是16位的數值,範圍是0到65535;在Linux2.4及以後版本中,使用32位儲存。
  4. 使用者列表(User list):這是以逗號分隔的使用者名稱列表,是該組的成員。(該組包含的是使用者名稱而不是user IDs,因為前面提過,在密碼檔案中,user IDs不一定是唯一的)。

如果使用者 avrusersstaffteach 組的成員,我們可能在密碼檔案中看到以下記錄:
在這裡插入圖片描述
在組檔案中看到以下記錄:
在這裡插入圖片描述
密碼檔案中的第四個欄位是group ID 100,表示 avr 是組 users 的成員。在組檔案中第四個欄位包含使用者名稱avr 的記錄也表示是該使用者的組。

8.4 Retrieving User and Group Information

本節中,我們看下可用於從密碼檔案、shadow密碼檔案和組檔案中獲取個人記錄的庫函式,以及瀏覽這些檔案中的所有記錄。

Retrieving records from the password file

getpwname()getpwuid() 函式用於從密碼檔案中獲取記錄。

# include <pwd.h>
//執行成功時返回指標,失敗時返回NULL,"未找到"時,請檢視下文的介紹
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);

getpwnam() 函式的name引數中傳入一個登入名,返回一個指向以下型別的結構體,對應於密碼檔案中的欄位資訊:

struct passwd 
{
    char *pw_name; /* Login name (username) */
    char *pw_passwd; /* Encrypted password */
    uid_t pw_uid; /* User ID */
    gid_t pw_gid; /* Group ID */
    char *pw_gecos; /* Comment (user information) */
    char *pw_dir; /* Initial working (home) directory */
    char *pw_shell; /* Login shell */
}

passwd 結構體中的 pw_gecos 和 pw_passwd 欄位沒有在SUSv3中定義,但是在所有UNIX實現中可用。只有當shadow password關閉時pw_passwd 欄位才有效。(程式設計中,確定shadow密碼是否開啟的最簡單方式是在成功呼叫getpwnam()後,再呼叫getspnam(),檢視傳入相同的使用者名稱是否會返回一個shadow密碼記錄。)一些其他的程式在該結構體中加入了其他額外的非標準欄位。

pw_gecos欄位的名稱來源於早期的UNIX實現,該欄位中的資訊用於與裝有通用綜合電子作業系統(General Electric Comprehensive Operating System,GECOS)的機子通訊。儘管這個用途老早就被廢棄了,但是這個欄位名稱卻沿用至今,用於記錄關於使用者的資訊。

getpwuid() 函式返回的資訊與 getpwnam() 一樣,只不過傳入的是引數是數值型的user ID。
getpwnam()和getpwuid()都返回一個指標,指向一個靜態分配的結構體。因此對於這兩個函式(下面介紹的getpwent()函式也是一樣)的呼叫都會覆蓋原來的結構體。

因為getpwnam()和getpwuid()返回指向靜態分配記憶體的指標,所以這兩個函式是不可重入(reentrant,21.1.2節)的。事實上,情況更加複雜,因為 返回的passwd結構體包含指向其他資訊(例如pw_name欄位)的指標,這些資訊也是靜態分配的。對於getgrnam()和getgrgid()也是相同情況。
SUSv3定義了與這些函式等價的一組可重入函式:getownam_r()、getpwuid_r()、getgrnam_r()和getgrgid_r()。它們有兩個引數passwd(或group)結構體和 一塊緩衝區,緩衝區用於儲存passwd(group)結構體中各欄位所指向的其他結構體。這塊緩衝所需要的位元組數可以通過呼叫 sysconf(_SC_GETPW_R_SIZE_MAX) (在組相關的函式中呼叫sysconf(_SC_GETGR_R_SIZE_MAX))獲取。更多詳情請參考這些函式的手冊頁。

SUSv3規定,如果沒有找到passwd中相匹配的的記錄,那麼getpwnam()和getpwuid()應該返回NULL,並且保持errno不變。這意味著我們需要使用如下的程式碼來區分 “出錯”“未找到” 這兩種情況:

struct passwd *pwd;

errno = 0;
pwd = getpwnam(name);
if (pwd == NULL) 
{
    if (errno == 0)
    	/* Not found */	
    else 
        /* Error */
}

但是很多UNIX實現沒有遵從SUSv3的這一點。如果想要匹配的passwd記錄沒有找到,那麼這些函式將返回NULL,並將errnor設定成非0值,例如ENOENT或者ESRCH。在版本2.7之前,glibc在這種情況下會產生ENOENT,但是從版本2.7開始,glibc遵從了SUSv3的規範。在各種實現中,出現這種差異性的原因主要是,POSIX.1-1990中非但沒有要求將函式遇到的錯誤設定成errno,反而允許將“未找到”這種情況設定為errno。這樣的結果就是,當使用這些函式時若要區分 “出錯”“未找到” 這兩種情況,將無法保證程式碼的可移植性。

Retrieving records from the group file

getgrnam()getgrgid() 函式用於從組檔案中獲取記錄。

#include <grp.h>
//執行成功時返回指標,失敗時返回NULL,"未找到"時,請檢視上文的介紹
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);

getgrnam() 函式用於通過 組名稱(group name) 查詢組資訊,getgrgid() 通過 組ID(group ID) 查詢組資訊。兩個函式都返回指向以下型別的結構體的指標:

struct group {
 char *gr_name; /* Group name */
 char *gr_passwd; /* Encrypted password (if not password shadowing) */
 gid_t gr_gid; /* Group ID */
 char **gr_mem; /* NULL-terminated array of pointers to names of members listed in /etc/group */
};

group結構體中的gr_passwd欄位沒有在SUSv3只定義,但是大部分UNIX實現支援該欄位。

跟上面描述的密碼函式一樣,每次呼叫這兩個函式都會覆蓋這個結構體。
如果這些函式不能找到匹配的組記錄,那麼它們的行為與上面介紹的getpwnam()和getpwuid()相同。

Example program

本節中我們介紹的這些函式的一個常見用途是使用者名稱(組名)和使用者ID(組ID)之間的轉換。Listing 8-1
Listing 8-1展示了這些轉換,有四種函式形式:userNameFromId()、userIdFromName()、groupNameFromId()和groupIdFromName()。

//Listing 8-1:Functions to convert user and group IDs to and from user and group names
//users_groups/ugid_functions.c
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include "ugid_functions.h" /* Declares functions defined here */

char *userNameFromId(uid_t uid) /* Return name corresponding to 'uid', or NULL on error*/
{
    struct passwd *pwd;
    pwd = getpwuid(uid);
    return (pwd == NULL) ? NULL : pwd->pwd_name;
}

uid_t userIdFromName(const char *name) /* Return UID corresponding to 'name', or -1 on error */
{
    struct passwd *pwd;
    uid_t u;
    char *endptr;
    if (name == NULL || *name == '\0') /* On NULL or empty string */
        return -1;                     /* return an error */
    pwd = getpwnam(name);
    if (pwd == NULL)
        return -1;
    return pwd->pw_uid;
}

char * groupNameFromId(gid_t gid) /* Return name corresponding to 'gid', or NULL on error */
{
    struct group *grp;
    grp = getgrgid(gid);
    return (grp == NULL) ? NULL : grp->gr_name;
}

gid_t groupIdFromName(const char *name) /* Return GID corresponding to 'name', or -1 on error */
{ 
    struct group *grp;
    gid_t g;
    char *endptr;
    
    if (name == NULL || *name == '\0') /* On NULL or empty string */
        return -1; /* return an error */

    grp = getgrnam(name);
    if (grp == NULL)
        return -1;
    return grp->gr_gid;
}

Scanning all records in the password and group files

setpwent()getpwent()endpwent 函式用於掃描 【scan,遍歷】 密碼檔案中的記錄。

#include <pwd.h>
// Returns pointer on success,or NULL on end of stream or error
stuct passwd *getpwent(void);

void setpwent(void);
void endpwent(void);

getpwent() 函式從密碼檔案中逐條 【one by one,一條條】 地返回資料,當沒有資料(或發生錯誤)時返回NULL。 第一次呼叫 getpwent() 函式時, 自動開啟密碼檔案。當掃描完之後,通過呼叫 endpwent() 關閉檔案。

struct passwd *pwd;
while ((pwd = getpwent()) != NULL)
 printf("%-8s %5ld\n", pwd->pw_name, (long) pwd->pw_uid);
endpwent();

endpwent()的呼叫是必要的,這樣隨後的getpwent()呼叫才會重新開啟密碼檔案,並從檔案起始位置開始讀取。此外 【on the other hand,另一方面】 ,如果我們已經瀏覽了部分檔案,可以使用 sepwent() 函式重新從檔案起始位置開始讀取。
getgrent()setgrent()endgrent() 函式用於對組檔案執行類似的任務。因為這些函式與密碼檔案的相關函式類似,我們就不再對這些函式 原型進行敘述。詳情請參考手冊頁。

Retrieving records from the shadow password file

下列函式用於獲取shadow密碼檔案中的單條記錄和掃描檔案中的所有記錄。

#include <shadow.h>
// Returns pointer on success, or NULL on not found or error
struct spwd *getspanm(const char *name);
// Returns pointer on success, or NULL on end of stream or error
struct spwd *getspent(void);
void setspent(void);
void endspent(void);

我們不會對上述 【these,這些】 函式進行詳細敘述,因為上述 【這些】 函式操作與對應的密碼檔案相關函式類似。(上述 【這些】 函式沒有在SUSv3中定義,且並沒有被所有UNIX實現所支援。)
getspnam()getspent() 函式返回指向spwd型別的結構體的指標,該結構體的形式如下:

struct spwd {
 char *sp_namp; /* Login name (username) */
 char *sp_pwdp; /* Encrypted password */
 /* Remaining fields support "password aging", an optional feature that forces users to regularly 
 change their passwords, so that even if an attacker manages to obtain a password, it will eventually
 cease to be usable. */
 long sp_lstchg; /* Time of last password change (days since 1 Jan 1970) */
 long sp_max; /* Max. number of days before change required */
 long sp_warn; /* Number of days beforehand that user is warned of upcoming password expiration */
 long sp_inact; /* Number of days after expiration that account is considered inactive and locked */
 long sp_expire; /* Date when account expires (days since 1 Jan 1970) */
 unsigned long sp_flag; /* Reserved for future use */
};

Listing 8-2 將展示 getspnam() 的用法。

8.5 Password Encryption and User Authentication

某些應用需要進行使用者驗證。驗證一般需要傳入使用者名稱(登入名)和密碼。出於這個目的,應用可能會將使用者名稱和密碼儲存到自己的資料庫中。 然而有時,可能需要使用者輸入 /etc/passwd 和 /etc/shadow中定義的使用者名稱和密碼。【are typical examples of such programs,這樣的例子有:】 例如 sshftp 這樣的網路應用會提供一種登入格式來連線到遠端系統,這就是此類程式的典範 。上述應用必須使用與標準login(登入)程式相同的方式,對使用者名稱和密碼加以 【進行】 驗證。
出於安全考慮,UNIX系統採用 【using,使用】 單向 加密演算法對密碼進行加密, 也就是說不能根據加密過的形式計算出原來的密碼。 因此, 驗證候選密碼( candidate password)的唯一方式是對它採用相同的方法進行加密, 然後使用加密後的結果與 /etc/shadow中的密碼 【value,值】 進行比對。 加密演算法被封裝在 crypt() 函式中。

#define _XOPEN_SOURCE
#include <unistd.h>
// 成功時返回指向一個靜態分配的字串的指標,該字串中儲存了加密後的密碼。遇到錯誤時返回NULL
char *crypt(const char *key, const char *salt);

crypt()演算法傳入一個最多8個字元的key(也就是:密碼),使用DES演算法的一種變體 【a variation of,變異演算法】 進行加密。salt引數是一個2個字元的字串,用於擾動(改變) 【混淆】 (perturb,vary)演算法,這是一種使加密密碼更加難以 【更難】 被破解的技術。函式返回指向一個靜態分配的13個字元的字串,內容 【也就】 是加密後的密碼。

正如之前所說,可以使用其他演算法替代DES。例如,MD5產生34位的字串,起始字元是美元符號($),這使得crypt()可以區分DES加密後的密碼和MD5加密後的密碼。
在密碼加密的探討中,我們使用的術語“加密(encryption)”的意思有點廣泛。確切地說,DES以給出的密碼字串為加密金鑰,編碼成固定位的字串。而MD5是一種複雜的雜湊函式 【a complex type of hashing function, 一個hash函式的複雜型別】 。這兩種加密方式的結果是一樣的:對輸入密碼進行難以破譯和不可逆轉的轉換。

譯者注:下圖可以看到我的Centos7上預設使用的是MD5加密演算法。可以看到我的Centos7上預設使用的是MD5加密演算法

salt引數和加密後的密碼都是從64個字符集[a-zA-Z0-9/.]中選取字元的組合。因此,2個字元的salt引數會使加密演算法生成 64*64=4096 種不同的變體。這意味著,預先對整部字典進行加密,再以其中的每個單詞與經過加密處理的密碼進行比對的做法不可行,破解程式需要對照4096種加密版本來檢查密碼。
crypt()函式返回的加密後的密碼的前兩個字元是原來的salt值。這就意味著,當加密一個候選密碼時,我們可以從儲存在/etc/shadow中的已經加密過的密碼中獲取合適的salt值。(當加密一個新的密碼時,程式(例如 passwd)會隨機產生一個salt值)。事實上,crypt()函式會忽視salt字串除前兩個字元外的其他字元。因此,我們可以直接將加密過的密碼作為salt引數傳入。

Example program

Listing 8-2 演示 【demonstrates,展示】 瞭如何使用crypt()來驗證 【authenticate,認證】 使用者。程式首先讀取使用者名稱,獲取相應的密碼記錄和(如果存在)shadow密碼記錄。如果沒有密碼記錄或者程式沒有許可權讀取shadow密碼檔案(這需要超級使用者特權或者shadow組的成員),那麼程式會打印出錯誤訊息。然後程式會使用getpass()函式讀取使用者的密碼。

#define _BSD_SOURCE
#include <unistd.h>
//成功時返回一個指標,指向靜態分配的儲存輸入密碼的字串,出錯時返回NULL
char *getpass(const char *prompt);

getpass() 函式首先會停止echo和所有終端特殊字元(例如中斷字元,一般是Ctrl-C)的處理(我們會在62章解釋如何改變這些終端設定)。然後打印出prompt指向的字串,讀取輸入的一行,返回以NULL結尾的輸入字串作為函式結果。(這個字串是靜態分配的,所以隨後呼叫getpass()會覆蓋這個值)。在返回之前,getpass()會將終端設定恢復成原來的狀態。
從getpass()中讀取了密碼之後,Listing 8-2中的程式會通過呼叫crypt()將輸入的密碼進行加密,然後跟shadow密碼檔案中相應的加密過的密碼進行匹配。如果能夠匹配上,則顯示使用者ID,例子如下:
在這裡插入圖片描述

Listing 8-2 中的程式使用sysconf(_SC_LOGIN_NAME_MAX)返回的值作為用於儲存使用者名稱的字元陣列的長度。sysconf(_SC_LOGIN_NAME_MAX)返回系統中使用者名稱的最大長度。我們會在11.2節敘述sysconf()的用法。

// Listing 8-2: Authenticating a user against the shadow password file
// users_groups/check_password.c
#define _BSD_SOURCE /* Get getpass() declaration from <unistd.h> */
#define _XOPEN_SOURCE /* Get crypt() delcaration from <unistd.h> */
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.h>
#include "tlpi_hdr.h"
int main(int argc, char *argv[])
{
    char *username, *password, *encrypted, *p;
    struct passwd *pwd;
    struct spwd *spwd;
    Boolean authOk;
    size_t len;
    long lnmax;
    lnmax = sysconf(_SC_LOGIN_NAME_MAX);
    if (lnmax == -1) /* If limit is indeterminate */
    	lnmax = 256; /* make a guess */
	username = malloc(lnmax);
	if (username == NULL)
		errExit("malloc");
	printf("Username: ");
	fflush(stdout);
	if (fgets(username, lnmax, stdin) == NULL)
		exit(EXIT_FAILUE);  /* Exit on EOF*/
	
	len = strlen(username);
	if (username[len - 1] == '\n')
		username[len - 1] = '\0'; /* Remove trailing '\n' */
	pwd = getpwnam(username);
	if (pwd == NULL)
		fatal("couldn't get password record");
	spwd = getspnam(username);
	if (spwd == NULL && errno == EACCES)
 		fatal("no permission to read shadow password file");
 	if (spwd != NULL) /* If there is a shadow password record */
 		pwd->pw_passwd = spwd->sp_pwdp; /* Use the shadow password */
 	
 	password = getpass("Password: ");
 	/* Encrypt password and erase cleartext version immediately */
 	encrypted = crypt(password, pwd->pw_passwd);
 	for (p = password; *p != '\0'; )
 		*p++ = '\0';
 	if (encrypted == NULL)
 		errExit("crypt");
 	authOk = strcmp(encrypted, pwd->pw_passwd) == 0;
 	if (!authOk)
 	{
 		printf("Incorrect password\n");
 		exit(EXIT_FAILURE);
	}
	printf("Successfully authenticated: UID=%ld\n", (long) pwd->pw_uid);
 	
 	/* Now do authenticated work... */
 	
 	exit(EXIT_SUCCESS);
} 

Listing 8-2 展示了一個重要的安全點。程式讀取密碼後應該立刻對密碼加密並且從記憶體中擦除未加密的版本。這樣,當程式一旦發生崩潰,能使從產生的核心dump檔案中讀取到密碼的可能性最小化。

還有其他的可能性使未加密的密碼暴露。例如如果虛擬記憶體頁包含的密碼被swapped out,特權程序就可以從swap檔案中讀取密碼。還有,具有足夠特權的程序可以通過讀取/dev/mem(一種虛擬裝置,可以將計算機中的實體記憶體以順序位元組流的形式呈現)來試圖發現密碼。
getpass()函式在SUSv2中出現,被標記為LEGACY,並註明該函式名具有誤導性,該函式提供的功能在任何情況下都容易實現。在SUSv3中刪除了getpass()的規範。但是大部分UNIX實現還是保留了對它的支援。

8.6 Summary

每個使用者都有唯一的登入名,和相關的數值型使用者ID。使用者可以屬於一個或多個組,每個組都有唯一的名稱以及對應的數值型識別符號。這些識別符號的主要目的是確立各種系統資源的所有者和訪問它們的許可權。
使用者的名稱和ID在/etc/passwd檔案中定義,該檔案還包含關於使用者的其他資訊。使用者和組之間的成員關係在/etc/passwd檔案的group欄位和/etc/group檔案中定義。此外,/etc/shadow檔案只能被特權程序讀取,用於將敏感的密碼資訊從公共可讀的/etc/passwd檔案中分離出來。多種庫函式可用於從這些檔案獲取資訊。
crypt()函式採用跟標準login程式相同的方式加密密碼,該函式在程式對使用者進行驗證時具有重要作用。

相關推薦

8 使用者

每個使用者都有唯一的 登入名 和相關的數值型 使用者識別符號(UID) 。使用者可以屬於一個或多個 組(group)。每個組都有唯一 名稱 和 組識別符號(GID) 。 UID和GID的主要目的是決定各種系統資源的所有權和控制程序訪問這些資源的許可權。例如,每個

Linux命令應用大詞典-8 日期時間

硬件時鐘 顯示 linu 時鐘 主機 style hwclock 硬件 暫停 8.1 cal:顯示日歷信息 8.2 date:顯示和設置系統日期和時間 8.3 hwclock:查看和設置硬件時鐘 8.4 clock:查看和設置硬件時鐘 8.5 clockdiff:主機之間

十節課:8iptablesfirewalld

        網絡卡配置:         物理機:192.168.10.1/24         伺服器:192.168.10.10/24      

Gemini Blueprint參考文件 8 打包部署基於Spring的OSGi應用

8.1.Bundle格式和Manifest頭 每一個應用模組都應打包成一個OSGi的bundle。bundle本質上是包含META-INF/MANIFEST.MF檔案的jar檔案,MANIFEST.MF檔案包含了OSGi服務平臺能夠識別的一系列頭資訊。參見OSGi服務平臺核心規範第3.2節瞭解細節。一些OS

servlet3.1規範翻譯:8 註解可插拔性

第8章 註解和可插拔性 本章討論在web應用中使用的Servlet 3.0規範定義的註解和啟用框架和庫的可插拔性增強。 8.1 註解和可插拔性 在web應用中,使用註解的類僅當它們位於WEB-INF/classes目錄中,或它們被打包到位於應用的WEB-INF/lib中的j

【C語言學習】《C Primer Plus》8 字符輸入/輸出輸入確認

multipl 字符輸入 信號 first while 目的 bcd 問題 img 學習總結 1、緩沖區分為完全緩沖區(fully buffered)I/O和行緩沖區(line-buffered)I/O。對完全緩沖輸入來說,當緩沖區滿的時候會被清空(緩沖區內容發送至

用戶管理權限

用戶 組 權限管理 筆記整理開始2018年4月3日17:27:07 本章內容: 解釋Linux的安全模型 解釋用戶賬號和組群賬號的目的 用戶的組管理命令 理解並設置文件權限 默認權限 特殊權限 ACL

《python基礎教程》2列表 讀書筆記

append() 列表方法 sort 追加 rev 基礎教程 筆記 buffer 刪除   第二章 列表和元組 1.數據結構:通過某種方式將元素集合在一起。 2.python的6種內建序列:列表,元組,字符串,Unicode字符串,buffer對象,xrange對象。 3.

:數字符串

ans can 輸入 是否 讀取字符串 下一步 字符數組 lag getc 2018-10-21-18:37:41 隨學筆記 小計 <1>:比較大的數組一般定義在main函數外面,否則程序很容易出錯。 <2>:memset 函數:   原型:void

資料基礎---《利用Python進行資料分析·2版》8 資料規整:聚合、合併重塑

之前自己對於numpy和pandas是要用的時候東學一點西一點,直到看到《利用Python進行資料分析·第2版》,覺得只看這一篇就夠了。非常感謝原博主的翻譯和分享。 在許多應用中,資料可能分散在許多檔案或資料庫中,儲存的形式也不利於分析。本章關注可以聚合、合併、重塑資料的方法。 首先

Oracle Database 12c DBA文官手冊(8版)——5 開發實現應用程式(續)

5.2、資源管理5.2.1、實現資料庫資源管理需要建立資源計劃、資源消費者組和資源計劃指令。使用資源管理器命令前須建立“未決區域”。針對會話啟用ADMINISTRATOR_RESOURCE_MAANAGER系統許可權將使用者賦給資源消費者組建立資源計劃指令分配相關資源1 切換消費者組2 使用SQL配置檔案5.

Oracle Database 12c DBA文官手冊(8版)——5 開發實現應用程式

1 調整設計:最佳實踐         1.1做盡可能 少的工作             應該簡化應用

Oracle Database 12c DBA文官手冊(8版)——5 開發實現應用程序(續)

調整 pac 生成 bms 百分比 列數 改變 應用程序 數據塊 5.2、資源管理5.2.1、實現數據庫資源管理需要創建資源計劃、資源消費者組和資源計劃指令。使用資源管理器命令前須創建“未決區域”。針對會話啟用ADMINISTRATOR_RESOURCE_MAANAGER系

2 系統使用者/管理(2) susudo

2.1 su 切換使用者或以指定使用者執行命令。 使用su可以指定執行命令的身份(user/group/uid/gid)。 為了向後相容,su預設不會改變當前目錄,且僅設定HOME和SHELL這兩個環境變數(若目標使用者非root,則還設定USER和LOGNAME環境變數)。推薦使用--

Atitit 現代資訊檢索 Atitit 重要章節 息檢索建模 檢索評價 8 文字分類 Line 210: 9 索引搜尋 11 Web檢索 13 結構化文字檢索 目錄 L

Atitit 現代資訊檢索   Atitit 重要章節 息檢索建模 檢索評價  第8章 文字分類 Line 210: 第9章 索引和搜尋 第11章 Web檢索 第13章 結構化文字檢索   目錄   Line 1

8 下半部推後執行的工作

核心為處理中斷而提供的中斷處理程式機制。中斷處理程式是核心中很有用的部分。但是,由於本身存在一些侷限,所以它只能完成整個中斷處理流程的上半部分。這些侷限包括: 中斷處理程式以非同步方式執行,並且有可能會打斷其他重要程式碼的執行。因此,為了避免被打斷的程式碼停止時間過長,中斷

C Primer Plus (五版)中文版—— 8 字元輸入/輸出輸入確認

8.1  單字元 I/O:getchar() 和 putchar() getchar() 和 putchar() 每次輸入和輸出一個字元。一個輸入回顯例子: /*使用一個while迴圈,該迴圈在遇到#時終止*/ int main(void) { char ch; while

快學Scala習題解答— 對映

4 對映和元組4.1 設定一個對映,其中包含你想要的一些裝備,以及它們的價格。然後構建另一個對映,採用同一組鍵,但是價格上打9折對映的簡單操作  Shell程式碼   scala> val map = Map("book"->10,"gun"->18

Netty原始碼分析8(高效能工具類FastThreadLocalRecycler)---->1節: FastThreadLocal的使用建立

  Netty原始碼分析第八章: 高效能工具類FastThreadLocal和Recycler   概述:           FastThreadLocal我們在剖析堆外記憶體分配的時候簡單介紹過, 它類似於JDK的T

Netty原始碼分析8(高效能工具類FastThreadLocalRecycler)---->2節: FastThreadLocal的set方法

  Netty原始碼分析第八章: 高效能工具類FastThreadLocal和Recycler   第二節: FastThreadLocal的set方法   上一小節我們學習了FastThreadLocal的建立和get方法的實現邏輯, 這一小節學習