日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

8. 用户和群组

發(fā)布時間:2024/3/7 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 8. 用户和群组 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

每個用戶都有唯一的登錄名和相關的用戶ID(UID)。用戶可以屬于一個或者多個群組。每一個群組有自己的名字和唯一的群組ID(GID)。

使用用戶和群組ID的主要目的就是能夠確定不同系統(tǒng)資源對不同目標群體的控制權限。比如說,一個文件屬于一個特定的用戶和群體,每一個進程也有自己所屬的用戶和群體ID,這時候通過匹配可以決定該進程是否可以擁有某些系統(tǒng)資源。

這一章我們將會看到用于決定用戶和群組的系統(tǒng)文件,然后我們會描述從這些文件當中提取信息的庫函數。最后我們會討論crypto()函數,它是一個用來加密和認證登陸密碼的函數。

8.1 密碼文件:/etc/passwd

系統(tǒng)文件password file,/etc/passwd,它每一行的內容都是一個用戶賬戶。每一行都有7個域,他們通過":"分隔,比如在我這里:

root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync ... pi:x:1000:1000:,,,:/home/pi:/bin/bash

上面包含了所有賬戶,其中找到我們在樹莓派上作為用戶來登錄的pi用戶。

我們現在按順序來一一講解這些fields都是什么,有什么意義:

  • Login name登錄名: 這是用戶為了登錄所需要使用的唯一名稱,通常被稱之為user name。相比于user ID我們可以將該login name設置為人可以理解的字符。使用ls(1)的話,可以顯示login name而不是數字user ID,比如 ls -l。可以看到擁有該文件訪問權的是user "pi"。
pi@raspberrypi:~/sysprog/learn_tlpi/build $ ls -l total 20 -rwxr-xr-x 1 pi pi 14020 Aug 3 21:52 out drwxr-xr-x 4 pi pi 4096 Aug 3 21:52 src
  • Encrypted password被加密密碼:這個域包含了一個13個字符的被加密的密碼(但是在我這里只顯示一個x,具體見該部分后半段),具體的見章8.5。如果password域包含了別的字符串,尤其是當不是13位的時候,那么登錄該賬戶的權限就會被凍結,因為該非13字符的域并不能代表一個有效的被加密密碼。注意,如果影子密碼被使能的話,這個位置則會顯示一個字母x,也就是我這里碰到的情況,同時影子密碼會被另外存在一個影子密碼文件當中,見8.2章。如果password域什么都沒有的話,那么就表明該賬戶登錄不需要任何密碼(在影子密碼被使能的情況下也是一樣)。
  • User ID(UID):這個就是在Login name中所提到的它所相對應的那個數字ID。這里有一個特殊的UID就是0,它代表著超級權限。通常來說0一般會被root賬戶所占據。在早期Linux 2.2版本之前,UID只是一個16位的數值從0-65535,但是現在他已經被在Linux2.4版本之后擴展到32位。
    另外,Login Name可以和User ID N:1映射,也就是說可以有多個登錄名和密碼使用同一個UserID,那么這也就同時允許多個用戶在使用不同的密碼的情況下有權進入同一個資源。甚至這些不同的用戶名還可以被分配在不同的用戶組ID里。
  • Group ID(GID):這個是上面UID所屬的第一個Group的ID。其他這個UID所屬的GID可以在系統(tǒng)group文件中找到。
  • Comment:這個域保存了一些關于該用戶文字性的內容,并且它可以通過比如finger(1)讀出來。
  • Home Directory:這是用戶登錄之后所被放置的初始文件夾的位置。
  • Login Shell:在用戶登陸之后,控制權就會被轉移到的程序。一般來說,他會是一個shell,比如bash,但是他也可以是任何其他程序。如果這個位置是空著的話,那么login shell就會被默認在/bin/sh上,也就是Bourne shell。這一個位置同時會變成SHELL環(huán)境變量的值。

在一個stand-alone系統(tǒng)(獨立系統(tǒng))上,所有的password信息都會存放在/etc/passwd里。然而,如果我們使用例如Network Inormation System的話或者別的系統(tǒng)的話,那么password就會被分布在網絡環(huán)境上面,部分這些信息會被保存在遠程系統(tǒng)上。

8.2 影子密碼文件:/etc/shadow

歷史上來說UNIX系統(tǒng)在/etc/passwd中維持著所有的用戶信息包括被加密的密碼。但是這種方式有著嚴重的security問題。因為很多有非高權限的系統(tǒng)都需要使用并得到該password文件中的一些信息,所以他就必須對所有用戶開放。這就相當于為破解密碼的程序開了一個后門,如果我們進行重復計算實驗總是能夠得到相應的密碼。因此/etc/shadow文件就孕育而生來阻止這一類攻擊。(其實一般來講密碼是通過HASH算法進行加密,當我們給定正確密碼的時候,計算機會計算出他的HASH值和存儲的HASH值進行比較,如果黑客知道用的哪一種標準算法,通過暴力破解,是可以通過HASH算出來所給的密碼是什么,所以這也是為什么要隱藏HASH值得原因)。因此這里就是將所有非敏感信息放在/etc/passwd文件當中,而被加密得密碼則維持在/etc/shadow當中,它只可以被有權限的程序讀取。

另外對于用戶名來說,它提供了/etc/passwd和/etc/shadow映射關系的角色,在shadow密碼文件當中他也提供了一些列包含信息安全相關的域。關于這些域的信息可以通過shadow(5)查找用戶手冊。我們這里只會關心被加密的密碼域,之后在8.5節(jié)當中會進行進一步的講解。

SUSv3并沒有要求要有shadow密碼,而且并不是所有的UNIX都實現了類似功能。

8.3 群組文件:/etc/group

處于管理的目的,尤其是當控制對文件以及其他系統(tǒng)資源訪問的時候,那么將用戶歸于某一些群組會非常有幫助。

用戶所屬的群組的集合被定義在/etc/passwd和/etc/group當中來表明該用戶屬于哪一個或者哪幾個群組。將這同一類信息分到兩個文件當中是出于歷史原因:在早期的UNIX實現中,一個用戶僅僅可以存在于一個Group當中。一個用戶的初始用戶組信息再登陸的時候就由password文件當中的GID所決定的,并且之后可以通過newgrp(1)來進行修改,當然為了達到這個目的需要提供Group的密碼才可以。從4.2BSD開始引入了多群組概念,后來成為了POSIX.1-1990標準。在這個概念底下,一個群組文件則被列在一個單獨的文件當中。

/etc/group群組文件,每一行都是一個單獨的群組信息。這里面由“:”隔出來四個域,其中包含了以下信息。首先參考樹莓派上所讀取出來的部分group的內容。

root:x:0: ... kmem:x:15: dialout:x:20:pi fax:x:21: voice:x:22: cdrom:x:24:pi floppy:x:25: tape:x:26: sudo:x:27:pi audio:x:29:pi dip:x:30: www-data:x:33: backup:x:34: operator:x:37: list:x:38: irc:x:39: src:x:40: gnats:x:41: shadow:x:42: utmp:x:43: video:x:44:pi sasl:x:45: plugdev:x:46:pi staff:x:50: games:x:60:pi users:x:100:pi nogroup:x:65534: systemd-journal:x:101: systemd-timesync:x:102: systemd-network:x:103: systemd-resolve:x:104: input:x:105:pi kvm:x:106: render:x:107: crontab:x:108: netdev:x:109:pi

書上的例子:

users:x:100: jambit:x:106:claus,felli,frank,harti,markus,martin,mtk,paul

按順序來解釋這些域:

  • Group Name:群組的字符名字,類似于password文件當中的login name。
  • Encrypted password:被加密密碼包含了群組的密碼(可選非必須)。如果一個用戶還不是該群組的成員的話,可以通過newgrp(1)來將將它加入到該群組當中,當然如果群組有密碼的話,需要在加的時候給出群組密碼。同樣如果shadow密碼被開啟的話,那么就像上面例子一樣,我們只會看到字符x,同樣被加密的密碼被保存在shadow group文件當中,/etc/gshadow(注意看這是另一個文件gshadow),當然這些信息也只有超級用戶和程序才能看到。group密碼和用戶密碼的加密方式相類似。
  • Group ID:這里就是對應著該字符Group name的數字GID。通常GroupID 0 只有一個對應的group name即 root。同樣的Linux 2.2版本之前只能支持最多65536個群組,但是現在新的版本里面可以支持到2的32次方個群組,就已經是一個天文數字了。
  • User list:最后一個域包含了這個群組包含了哪些用戶成員。這些用戶成員是以用戶成員名字所寫而不是用戶成員ID,因為像我們之前所說的,用戶成員ID在user password文件當中并不是唯一。

記錄avr是群組users, staff和teach的成員,我們可以在password文件中看到這樣的內容:

avr:x:1001:100:Anthony Robins:/home/avr:/bin/bash

群組文件中:

users:x:100:avr staff:x:101:mtk,avr,martinl teach:x:104:avr,rlb,alc

原書上users后面沒有跟任何東西,我認為這里應該是一個錯誤。如果對比我的樹莓派的group的話,pi是users的成員。

8.4 獲取用戶和群組信息

在這一段落當中,我們會看到一些允許我們通過庫函數,從密碼文件,影子密碼文件以及群組文件當中獲取一些單獨信息的辦法。

獲取密碼文件中的記錄

使用getpwnam()和getpwuid()就可以從密碼文件當中讀取出來需要的信息。

#include <pwd.h> struct passwd *getpwnam(const char *name); struct passwd *getpwuid(uid_t uid);

當我們給定login name也就是字符登錄名的時候,getpwnam()就可以返回一個指向以下結構體類型的指針,它包含了以下信息:

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 */ };

pw_gecos和pw_passwd并沒有被定義在SUSv3,但是目前所有UNIX實現都支持它們。pw_passwd在影子密碼沒有被開啟的時候才會包含有效信息。為了能夠了解是否影子密碼被開啟,我們可以在getpwnam()的調用之后調用getspnam(),來看是否所對應的用戶名包含了影子密碼。一些其他的實現可能還會包含一些額外的信息。

getpwuid()函數返回的信息和getpwnam()是一致的,但是是通過給定的uid來返回內容。getpwnam()和getpwuid()返回一個被指向靜態(tài)分配的結構的指針。該結構體可以被對這些函數的每次調用而被改變。因為他們返回一個指向靜態(tài)分配內存的指針,getpwnam()和getpwuid()并不可再進入(not reentrant,這里我沒搞明白,一般一個函數在執(zhí)行過程中間,可能也許因為中斷的原因,被另一個函數再執(zhí)行以下這個函數就叫做reentrant)。事實上,這情況可能會更復雜,因為被返回的passwd結構包含了指向其它信息的指針,比如pw_name也是被靜態(tài)分配的。關于reentrancy這個話題會在21.1.2被提起。類似的語句還有getgrnam()和getgrgid()函數。

SUSv3定義了一套等效但是可reentrant的函數-getpwnam_r(),getpwuid_r(),getgrnam_r(),getgrgid_r()。

根據SUSv3,如果沒有找到相應的passwd記錄的話,那么getpwnam()和getpwuid()會返回NULL作為錯誤代碼,但是errno不會有任何改變。這也就意味著我們可以根據以上信息區(qū)分錯誤或者“沒找到”的情況,代碼用例:

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

然而,很多UNIX的實現在這點上并不符合SUSv3。如果一個符合的passwd記錄滅有被找到的話,這些函數就會返回NULL并且設定errno為一個非零的數字,比如ENOENT或者ESRCH。總的來說對于可移植程序來講,實現這樣完全兼容的區(qū)分error和找不到的情況是比較困難的。

從群組文件中獲得記錄

getgrnam()和getgrgid()函數從群組文件中獲得記錄。

#include <grp.h> struct group *getgrnam(const char *name); struct group *getgrgid(gid_t gid); //Both return a pointer on success, or NULL on error; //see main text for description of the “not found” case

getgrnam()函數通過group name查詢群組信息,getgrgid()函數通過GID查找信息。兩個函數都返回一個指向以下類型結構體的函數:

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 namesof members listed in /etc/group */ };

同上一節(jié)password函數一樣,這個結構體也會在調用這兩個函數的時候被改寫。如果函數找不到一個匹配的group的話,那么他們就會有與getpwnam()和getpwuid()相同的行為。

例程

一個通用的對上述函數的使用方法就是轉換文字用戶和群組名稱到數字ID上或者反之亦然。下面的例子用四個函數給出了一個這樣的轉換:userNameFromId(),userIdFromName(),groupNameFromId()還有groupIdFromName()。

users_groups/ugid_functions.c (from "The Linux Programming Interface")

#include <pwd.h> #include <grp.h> #include <ctype.h> #include "ugid_functions.h" /* Declares functions defined here */ char * /* Return name corresponding to 'uid', or NULL on error */ userNameFromId(uid_t uid) {struct passwd *pwd;pwd = getpwuid(uid);return (pwd == NULL) ? NULL : pwd->pw_name; } uid_t /* Return UID corresponding to 'name', or -1 on error */ userIdFromName(const char *name) {struct passwd *pwd;uid_t u;char *endptr;if (name == NULL || *name == '\0') /* On NULL or empty string */return -1; /* return an error */u = strtol(name, &endptr, 10); /* As a convenience to caller */if (*endptr == '\0') /* allow a numeric string */return u;pwd = getpwnam(name);if (pwd == NULL)return -1;return pwd->pw_uid; } char * /* Return name corresponding to 'gid', or NULL on error */ groupNameFromId(gid_t gid) {struct group *grp;grp = getgrgid(gid);return (grp == NULL) ? NULL : grp->gr_name; } gid_t /* Return GID corresponding to 'name', or -1 on error */ groupIdFromName(const char *name) {struct group *grp;gid_t g;char *endptr;if (name == NULL || *name == '\0') /* On NULL or empty string */return -1; /* return an error */g = strtol(name, &endptr, 10); /* As a convenience to caller */if (*endptr == '\0') /* allow a numeric string */return g;grp = getgrnam(name);if (grp == NULL)return -1;return grp->gr_gid; }

users_groups/ugid_functions.h (from "The Linux Programming Interface")

/* ugid_functions.hHeader file for ugid_functions.c. */ #ifndef UGID_FUNCTIONS_H #define UGID_FUNCTIONS_H#include "tlpi_hdr.h"char *userNameFromId(uid_t uid);uid_t userIdFromName(const char *name);char *groupNameFromId(gid_t gid);gid_t groupIdFromName(const char *name);#endif

掃描所有在password和group文件當中的記錄

setpwent(),getpwent()還有endpwent()函數都可以用于順序掃描password文件當中的記錄。

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

getpwent()函數從password文件當中逐一返回記錄,當如果沒有繼續(xù)其他的記錄的話它將會返回NULL。在第一次調用的時候,getpwent()會自動打開password文件。當我們結束這個文件讀取的時候,應該調用endpwent()來結束它。

我們可以歷遍整個密碼文件來輸出登錄名和userID:

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

endpwent()作為對getpwent()調用的接續(xù)來說很重要,因為這樣子在endpwent()之后就可以再次利用getpwent()打開文件并定位在文件起始位置。另外,如果我們已經到達文件中間,如果我們使用setpwent()的話就可以再次從頭再開始。

getgrent(),setgrent()以及endgrent()函數做與上述函數類似的行為。

從影子文件當中獲取記錄

下面的函數被用于從影子文件中獲取每一個記錄并且用于掃描文件中所有記錄。

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

他們的使用方法和上面章節(jié)類似。只是需要注意的是,這里的spwd結構體函數類型如下:

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

8.5 密碼加密和用戶驗證

一些應用需要用戶授權。驗證一般來講會使用用戶名(login name)和密碼的形式。一個應用可能會維持它自己的用戶名和密碼的數據庫。這個時候,可能對于用戶能夠進入/etc/passwd和etc/shadow中獲取用戶和密碼是一件必要的事情。當然網絡應用可能它的用戶名和密碼儲存在遠程系統(tǒng)上,比如ssh和ftp。

從信息安全的角度來說,UNIX系統(tǒng)使用一次加密算法來加密密碼。也就是說沒有辦法通過它加密的方式來反推出密碼。因此驗證密碼的方式就是,將輸入的密碼以同樣的方式加密,來比較它的值是否與存儲在/etc/shadow中的值一致。這樣的加密算法是通過調用crypt()函數而實現的。

#define _XOPEN_SOURCE #include <unistd.h> char *crypt(const char *key, const char *salt); Returns pointer to statically allocated string containing encrypted password on success, or NULL on error

crypt()算法可以輸入一個最多8位字符的key(也就是密碼),然后通過不同的數據加密標準算法DES來進行計算。第二個參數salt是一個由兩個字符組成的字符串,它用來改變算法的一些輸入值,這樣子就可以進一步加強對密碼加密被破解的難度。該方法返回一個指向13個字符的字符串的被靜態(tài)分配內存的地址,這里面存儲著被加密的密碼。

salt參數和加密密碼都是由從這64個字符中[a-zA-Z0-9/.]中所選取的字符所組成。所以2個字符的salt參數就可以使得加密算法由64x64=4096中不同的變化。也就是說就算是同一個密碼,因為salt的不同,他也有4096種被加密出來的不同結果。

crypt()在調用之后會返回給定的salt和被加密的密碼。也就是說當加密一個password的時候,我們就可以從存儲在/etc/shadow中的被加密密碼中找到我們要用的salt值。當然在給入salt的時候,它只會節(jié)選所輸入所有字符的前兩個字符作為salt,所以這也就提供了另外一種可能性,那就是直接選取密碼的前兩個字符作為salt。為了能夠使用crypt()函數,我們需要在編譯程序的時候顯式使用-lcrypt選項,這樣子就可以鏈接到crypt庫上

例程

下面的程序驗證如何使用crypt()來驗證用戶的方法。該程序讀取用戶名然后再提取相應的密碼記錄和影子密碼記錄。在沒有密碼記錄存在的情況下,該程序會打印出來錯誤并且退出。為了讀取用戶密碼,我們會使用以下函數:

#define _BSD_SOURCE #include <unistd.h> char *getpass(const char *prompt); //Returns pointer to statically allocated input password string //on success, or NULL on error

getpass()函數首先關閉terminal里的echo功能并且所有處理terminal特殊字符的功能(比如中斷也就是control+C)。然后它會打印prompt的內容,并且讀取輸入行,并且返回一個帶'\0\n'的字符串。在返回之前,getpass()會存儲終端設定到它的原始存儲當中。

在用getpass()讀過密碼之后,程序就會繼續(xù)用crypt()加密所輸入密碼再來驗證它。如果密碼符合的話,那么用戶的ID就會被展示出來。
users_groups/check_password.c (from "The Linux Programming Interface")

#define _BSD_SOURCE /* Get getpass() declaration from <unistd.h> */ #define _XOPEN_SOURCE /* Get crypt() declaration 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_FAILURE); /* 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); }

注意:

  • 這里編譯的時候不要忘記在link的時候添加-lcrypt到LDFLAGS上。
  • 如果使用樹莓派的話,如果是第一次使用root權限,因為樹莓派的Raspbian系統(tǒng)默認root是禁用狀態(tài),而且沒有密碼,所以需要先設置密碼再開啟root允許。
    1. sudo passwd root 設置密碼
    2. sudo passwd --unlock root 開啟root允許
    3. su root (會提示輸入密碼)登錄
    最后. 使用完后可以使用sudo passwd --lock root來鎖定root權限

    之所以這么做,是因為上面程序需要有高權限。

完整使用過程如下:

pi@raspberrypi:~/sysprog/learn_tlpi $ su root Password: root@raspberrypi:/home/pi/sysprog/learn_tlpi# cd build root@raspberrypi:/home/pi/sysprog/learn_tlpi/build# ./out Username: pi Password: Successfully authenticated: UID=1000 root@raspberrypi:/home/pi/sysprog/learn_tlpi/build#

程序大概執(zhí)行過程如下:

  • 設定讀取文件名的最大值
  • 在heap中為用戶名分配以最大文件名為范圍的內存
  • 通過terminal輸入用戶名,這里是pi,并將最后的\n替換成\0
  • 通過getpwnam(用戶名)從password文件中得到該用戶名的記錄的指針,若不存在退出
  • 通過getspnam(用戶名)從shadow文件中得到該用戶名的密碼哈希值的指針,若不存在或沒權限退出
  • 將shadow讀出來的密碼hash值寫入password讀出來的記錄的密碼位(本身password讀出來的是x)
  • 利用getpass()手動輸入密碼,它會自動將輸入密碼放在一個分配好的內存上,而且在輸入過程中不會顯式出來所輸入密碼,最后并返回一個指向密碼的指針。
  • 利用crypt()將輸入密碼和hash值前兩位作為salt進行加密
  • 循環(huán)已輸入密碼的內存地址到最后一位'\0',將'\0'之前所有內容替換成'\0'以達到破壞已知密碼的目的 root@raspberrypi:/home/pi/sysprog/learn_tlpi/build# ./out Username: pi Password: password input: 1234567 Incorrect password root@raspberrypi:/home/pi/sysprog/learn_tlpi/build#

    在此之前,如果使用printf輸入所輸入的密碼,即使它實在一個被getpass所分配的內存上所儲存,我也是可以讀出來我給定的是什么。這個應該可以做到類似于windows上點一下密碼右邊的眼睛知曉我給定的是什么密碼,確定輸入無誤。但是無論如何在輸入完成之后一定要破壞掉已輸入密碼。如果不是立即破壞掉的話,如果程序崩潰,core dump又可讀的話,明文密碼則一定會泄露。
    泄密當然還有別的可能性,比如說一個包含了密碼的虛擬內存頁在被交換出去的時候可能會被有權限的程序從swap文件中讀到。另外,當一個有足夠權限的進程讀取/dev/mem(一個表現計算機物理內存的虛擬設備,里面有一連串字節(jié))的時候,可能可以從里面發(fā)現密碼。

  • 對比hash值和新加密密碼的值,如果一致則可以進行下一步(這里就是簡單說下驗證成功)或者直接退出程序

練習

總結

以上是生活随笔為你收集整理的8. 用户和群组的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。