第六章 系統數據文件和信息
系統正常運行需要使用大量系統數據文件,其中,所有UNIX系統都有的是:口令文件、組文件,大多數系統都提供的是:登錄賬戶記錄、系統標識、時間和日期例程,同時還有其他一些常用系統數據文件如:BSD網絡軟件有一個記錄各網絡服務器所提供服務的數據文件(/etc/services)、記錄協議住處的數據文件(etc/protocols)等。
現在來按這個順序討論這些文件。
1、口令文件
口令文件包括了以下字段,這些字段包含在<pwd.h>中定義的passwd結構中:
說明 |
struct passwd成員 |
用戶名 |
char *pw_name |
加密口令 |
char *pw_passwd |
數值用戶ID |
uid_t pw_uid |
數值組ID |
gid_t pw_gid |
註釋字段 |
char *pw_gecos |
初始工作目錄 |
char *pw_dir |
初始shell(用戶程序) |
char *pw_shell |
用戶訪問類 |
char *pw_class |
下次更改口令時間 |
time_t pw_change |
賬戶到期時間 |
time_t pw_expire |
由於歷史原因,口令文件存儲在/etc/passwd中,並且是一個ASCII文件。每一行表示上表的各個字段,字段之間用冒號分隔。例如按照上面的字段順序,passwd文件中可能會有如下內容:
squid:x:23:23::/var/spool/squid:/dev/null
sar:x:205:105:Litingli:/home/sar:/bin/bash
注:
l 如果是root用戶,則其用戶數值ID爲0
l 口令文件項中某些字段可以爲空
l shell字段包含一個可招待程序名,它被用於用戶登錄shell,若該字段爲空,則取系統默認值。注意到squid中shell字段被設置爲/dev/null這是一個設備,不能執行,這樣做的目的是阻止任何人以用戶squid的名義登錄到該系統
某些UNIX系統提供finger(1)命令可用於查看註釋字段中的信息,如對於以下記錄:
sar:x:205:105:Litingli,scu,88-8888,66-6666:/home/sar:/bin/bash
註釋字段的各個信息以逗號隔開:用戶姓名,辦公地點,辦公電話,家庭電話等,使用finger(1)命令就可以打印註釋字段中的信息:
#finger –p sar
Login:sar Name:Litingli
Directory:/home/sar Shell:/bin/sh
Office:88-8888 Home Phone:66-6666
On since Mon Jan 19 03:57 (EST) on ttyv0 (message off)
No mail
1.1查詢口令文件
查詢口令文件有兩種方式:一種僅查詢特定用戶的記錄,一種是查詢文件中所有記錄。
對於特定用戶的查詢,可以使用以下函數完成:
#include <pwd.h>
struct passwd *getpwuid(uid_t uid); //通過用戶ID查詢
struct passwd *getpwnam(const char* name); //通過用戶名查詢
對於所有記錄的查詢,可能使用以下函數完成:
#include <pwd.h>
struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);
調用getpwent時,返回口令文件中的下一個記錄;setpwent反繞它所使用的文件,所謂反繞,是指使getpwent指向口令文件的開頭,使其回到起點讀取;最後,一定要使用endpwent關閉這些文件。
1.2編輯口令文件
某些系統提供vipw命令,允許管理員使用該命令編輯口令文件,此命令串行化對口令文件所做的修改(有點類似於鎖的概念,串行化後,不會存在同時多個對口令文件的寫操作),並確保所做的更改與其他相關文件保持一致。
2、陰影口令
之所以加入陰影口令,是出於安全的考慮,因爲加密口令是經單向加密算法處理用戶口令副本,由於其不可逆性,因此不能夠將加密口令經過逆向算法還原成明文口令,但是這樣也不能防止一些不法份子來猜口令,只要通過單向加密算法將猜測的明文口令變換成加密形式,然後將其與合法用戶的加密口令對比,即可猜出(譬如不法份子可以申請一個賬戶,然後不斷地修改其口令,每次修改後都查看一下口令文件中被加密的口令,看是否和合法用戶的加密口令一樣,那麼他就找到了合法用戶的明文口令了)。
可以發現,上面之所以不法份子能猜到合法用戶的口令,是因爲他可以查看口令文件中的加密口令,然後和他自己口令的加密形式來對比,就能猜出合法用戶的口令。所以,只要將加密口令隱藏起來,使其無法獲得原始資料,不法份子也就沒法比較了。
這是就引入陰影口令的原因,現在我們將加密口令存放於陰影口令文件,而非前面提到的口令文件。陰影口令文件不是一般用戶可以讀取的,例如login(1)和passwd(1),這些函數必須要用戶ID爲root才能存取加密口令,所以就樣就能很好的保護好“原始資料”。
明白了其工作原理,來看看其細節。陰影口令文件至少要包括用戶名和加密口令。與該口令相關的其他信息也可存放在該文件中。在Linux2.4.22和Solaris 9中,有一組函數可以訪問口令文件(其實這和前面查看口令文件的函數類似):
#include <shadow.h>]
struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
FreeBSD5.2.1和MAC OS X10.3中,沒有陰影口令結構。
3、組文件
UNIX組文件包含了以下字段:
說明 |
struct group成員 |
組名 |
char *gr_name |
加密口令 |
char *gr_passwd |
數值組ID |
int gr_gid |
指向各用戶名的指針數組 |
char **gr_mem |
同口令文件一樣,組文件也分爲兩種查詢方式:
對於特定組的查詢可用以下函數:
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
對於整個組文件的查詢,可用以下三個函數,功能與口令文件的類似:
#include <grp.h>
struct group *getgrent(void);
void setgrent(void);
void endgrent(void);
4、附加組ID
爲什麼會出現一個附加組ID?這次和陰影口令不一樣,並非出於安全的考慮。在傳統UNIX中,每個用戶任何時候只能屬於一個組,當用戶登錄時,系統就會去查看口令文件中“數值組ID”這項,然後將此組ID賦於用戶實際組ID(有點類似於一個人只能有一種國籍,要麼是中國國籍,要麼就是別國國籍)。這種組成員形式一直維持到1983年左右,後來在BSD4.2中引入了附加組ID,用戶不僅可以屬於口令文件中記錄的組ID,而且還可以屬於其他的組(這就類似於一個人在取得中國國籍之後,還可以取得其他國家的國籍)。
附加組ID的優點在於:不必顯式地經常更改組。可以通過以下三個函數來獲取和設置組ID:
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
#include <grp.h>
#include <unistd.h>
int setgroups(int ngroups, const gid_t grouplist[]);
int initgroups(const char *username, gid_t basegid);
對於這些函數不再做過多說明。
另外,需要說明的是,在很多系統中,用戶和組數據庫是用網絡信息服務(Network Information Service ,NIS)實現的,這使管理員可編輯數據庫的主副本,然後將它分發到組織中的所有服務器上,客戶端系統可以聯繫服務器以查看用戶和組的有關信息。
5、登錄賬戶記錄
大多數UNIX系統者提供下列兩個數據文件:utmp和wtmp文件,分別用於記錄當前登錄進系統的各個用戶,跟蹤各個登錄、註銷事件。在V7中,這兩個文件都包含如下結構:
struct utmp{
char ut_line[8]; /*tty line: “ttyh0”,”ttyd0”,”ttyp0”,…*/
char ut_name[8]; /*login name*/
long ut_time; /*seconds since Epoch*/
};
登錄時,login程序填寫此類型結構,然後將其寫入到utmp文件中,同時也添寫其 wtmp文件中;註銷時,init進程將utmp文件中相應的記錄擦除(每個字節都填爲0),並將一個新記錄添寫到wtmp文件中,在wtmp文件的註銷記錄中,將ut_name字段清0;在系統重啓、更改系統時間和日期前後,都在wtmp文件中添寫特殊的記錄項。
who(1)程序讀utmp文件,並以可讀格式打印其內容;後來的UNIX版本提供了last(1)命令,它讀wtmp文件並打印所選擇的記錄。
6、系統標識
可以通過uname函數得到當前主機和操作系統的有關信息:
#include <sys/utsname.h>
int uname(struct utsname *name);
該函數所用的數據結構如下(下面列出的只是POSIX.1定義的至少需要的字段,某些實現在該結構中提供了另外一些字段):
struct utsname{
char sysname[]; /*name of OS*/
char nodename[]; /*name of this node*/
char release[]; /*current release of operating system*/
char version[]; /*current version of this release*/
char machine[]; /*name of the hardware type*/
};
另外,BSD系統提供了gethostname函數(目前它已是POSIX.1的一部份),它只返回主機名,該名字通常是TCP/IP網絡上主機的名字。
當然除了可以獲取主機名之外,我們還需要設置主機名,hostname(1)命令可以二者都實現,超級用戶則可以使用一個類似的函數sethostname用於設置主機名。
7、時間和日期例程
UNIX內核提供的基本時間服務是計算處國際標準時間公元1970年1月1日00:00:00以來經過的秒數,它是用數據類型time_t表示的,我們稱其爲日曆時間:包括日期和時間。
time函數可以返回當前時間和日期:
#include <time.h>
time_t time(time_t *calptr);
與time函數相比,gettimeofday提供了更高的分辨率(最高爲微秒級):
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
其中,結構timeval定義如下:
struct timeval{
time_t tv_sec;
long tv_usec; /*microseconds*/
};
一旦獲得這種以秒計的整型時間值後,通常要調用另一個時間函數將其轉換爲人們可讀的時間和日期,下圖說明了各個函數之間的關係:
結構tm定義如下(注意每個成員的範圍):
struct tm{ /*a broken-down time*/
int tm_sec; /*seconds after the minute:[0-60]
int tm_min; /*minutes after the hour:[0-59]
int tm_hour; /*hours after midnight:[0-23]*/
int tm_mday; /*day of the month[1-31]*/
int tm_mon; /*months since January[0-11]*/
int tm_year; /*years since 1900*/
int tm_wday; /*days since Sunday:[0-6]*/
int tm_yday; /*days since January 1:[0-365]*/
int tm_isdst; /*datlight saving time flag:<0,0,>0*/
};
8、其他數據結構
在開頭,已經提到了還有一些其他常用的數據文件,如:BSD網絡軟件有一個記錄各網絡服務器所提供服務的數據文件(/etc/services)、記錄協議住處的數據文件(etc/protocols)等。幸運的是,針對這些數據的端口都與口令文件和組文件接口相似
1、 set函數,打開相應數據文件,然後反繞該文件。
2、 end函數,關閉相應數據文件。