6.1 引言node
UNIX系統的正常運行須要使用大量與系統有關的數據文件,針對這些數據文件的可移植接口是本章的主題。本章還介紹了系統標識函數、時間和日期函數linux
6.2 口令文件算法
UNIX系統的口令文件包含了下列各字段,這些字段包含在<pwd.h>中定義的passwd結構中shell
用戶名 char *pw_name數據庫
加密口令 char *pw_passwd數組
數值用戶ID uid_t pw_uid安全
數值組ID gid_t pw_gidbash
註釋字段 char *pw_gecos服務器
初始工做目錄 char *pw_dir網絡
初始shell char *pw_shell
用戶訪問類 char *pw_class
下次更改口令時間 time_t pw_change
帳戶到期時間 time_t pw_expire
因爲歷史緣由,口令文件存儲在/etc/passwd中,並且是一個ASCII文件。
linux中,可能有下列四行
root:x:0:0:root:/root:/bin/bash
squid:x:23:23: :/var/spool/squid:/dev/null
nobody:x:65534:65534:Nobody:/home:/bin/sh
sar:x:205:105:Stephen:/home/sar:/bin/bash
關於這些登錄項請注意下列各點:
-一般一個用戶名爲root的登錄項,其用戶ID是0
-加密口令字段包含了一個佔位字符(之前加密口令直接放在該字段,但因爲安全問題,如今放在另外一位置)
-口令文件項中的某些字段多是口。若是加密口令字段爲空,一般意味着該用戶沒有口令。
-shell字段包含了一個可執行程序名,它被用做該用戶的登錄shell。若爲空,則取默認值,一般爲/bin/sh。(注意,squid登錄項的該字段爲/dev/null.顯然,這是一個設備,不能
執行,所以將其設於此處的目的是,阻止任何人以用戶squid的名義登陸到該系統
-爲了阻止一個特定用戶登錄系統,除使用/dev/null以外,還有若干種替代方法。一種常見的方法是,將/bin/false用做登錄shell。它簡單地以不成功(非0)狀態終止,該shell將此種狀態判斷爲假。另外一種常見的方法是,用/bin/true禁止一個帳戶。它所作的一切是以成功(0)狀態終止。某些系統提供nologin命令,它打印可自定義的出錯信息,而後以非0狀態終止
-使用nobody用戶名的目的是,使任何人均可登錄至系統,但其用戶ID(65534)和組ID(65534)不提供任何特權。該用戶ID和組ID只能訪問人人皆可讀寫的文件
-提供finger(1)命令的某些UNIX系統支持註釋字段中的附加信息。
POSIX.1只定義了兩個獲取口令文件項的函數。在給出用戶登錄名或數值用戶ID後,這兩個函數就能查詢相關項。
#include<pwd.h> struct passwd *getpwuid(uid_t uid); struct passwd *getpwnam(const char *name); //返回值:若成功則返回指針,若出錯或到達文件結尾則返回NULL
getpwuid函數由ls(1)程序使用,它將i節點中的數值用戶ID映射爲用戶登錄名。在鍵入登錄名時,getpwnam函數由login(1)程序使用。
若是要查看整個口令文件,下列三個函數則可用於此種目的
#include<pwd.h> struct passwd *getpwent(void); //返回值:若成功則返回指針,若出錯或到達文件結尾則返回NULL void setpwent(void); void endwent(void);
調用getpwent時,它返回口令文件中的下一個記錄項。如同上面所述的兩個POSIX.1函數同樣,它返回一個由它填寫好的password結構的指針。每次調用此函數時都重寫該結構。在第一次調用該函數時,它打開它所使用的各個文件。
函數setpwent反饒它所使用的文件,endpwent則關閉這些文件。在使用getpwent查看完口令後,必定要調用endpwent關閉這些文件。getpwent知道什麼時間它應當打開它所使用的文件(第一次被調用時),但它不知道什麼時候關閉這些文件
實例:6_1 getpwnam函數的實現
1 #include<pwd.h> 2 #include<stddef.h> 3 #include<string.h> 4 struct passwd * getpwnam(const char *name) 5 { 6 struct passwd *ptr; 7 setpwent(); 8 while((ptr=getpwent())!=NULL) 9 if(strcmp(name,ptr->pw_name)==0) 10 break; 11 endpwent(); 12 return(ptr); //ptr is NULL if no match found 13 }
在程序開始處調用setpwent是自我保護性的措施,以便在調用者在此以前已經調用getpwent打開了有關文件的狀況下,將有關文件定位到文件開始處。getpwnam和getpwuid調用完成後不該使有關文件仍處於打開狀態,因此應調用endpwent關閉它們
6.3 陰影口令
加密口令是經單向加密算法處理過的用戶口令副本。所以此算法是單向的,因此不能從加密口令猜想到原來的口令。但人們能夠用試探辦法猜想口令。。。
爲使企圖這樣作的人難以得到原始資料(加密口令),如今,某些系統將加密口令存放在另外一個一般稱爲陰影口令的文件中。該文件至少要包含用戶名和加密口令。
etc/shadow文件中的字段:
用戶登錄名 char *sp_namp
加密口令 char *sp_pwdp
上次更改口令以來通過的時間 int sp_lstchg
通過多少天后容許更改 int sp_min
要求更改尚餘天數 int sp_max
要求警告天數 int sp_warn
帳戶不活動以前尚餘天數 int sp_inact
帳戶到期天數 int sp_expire
保留 unsigned int sp_flag
陰影口令文件不是通常用戶能夠讀取的,僅有少數幾個程序須要存取加密口令,例如login(1)和passwd(1),這些程序經常是設置用戶ID爲root的程序。有了陰影口令後,普通口令文件/etc/passwd可由用戶自由讀取
6.4 組文件
UNIX組文件中的字段包含在<grp.h>中所定義的group結構中
/etc/group文件中的字段:
組名 char *gr_name
加密口令 char *gr_passwd
數值組ID int gr_gid
指向各用戶的指針的數組 char **gr_mem
字段gr_mem是一個指針數組,其中每一個指針各指向一個屬於該組的用戶名。該數組以空指針結尾。
能夠用下列兩個由POSIX.1定義的函數來查看組名或數值組ID
#include<grp.h> struct group *getgrgid(gid_t gid); struct group *getgrnam(const char *name); //兩個函數返回值:若成功則返回指針,所出錯則返回NULL
若是須要搜索整個組文件,則需使用另外幾個函數。下列三個函數相似於針對口令文件的三個函數
#include<grp.h> struct group *getgrent(void); //返回值:若成功則返回指針,若出錯或到達文件結尾則返回NULL void setgrent(void); void endgrent(void);
setgrent函數打開組文件(如若它還沒有被打開)並反饒它。getgrent函數從組文件中讀下一個記錄,如若該文件還沒有打開則先打開它。endgrent函數關閉組文件
6.5 附加組ID
引入了附加組ID的概念,咱們不只能夠屬於口令文件記錄項中組ID所對應的組,也可屬於多達16個另外的組。文件訪問權限檢查相應被修改成:不只將進程的有效組ID與文件的組ID相比較,並且也將全部附加組ID與文件組ID相比較。
使用附加組ID的優勢是沒必要再顯示地常常更改組。一個用戶會參加多個項目,所以也就要同時屬於多個組。此類狀況是常常有的。
爲了獲取和設置附加組ID,提供了下列三個函數:
#include<unistd.h> int getgroups(int gidsetsize,gid_t grouplist[]); //返回值:若成功則返回附加組ID數,若出錯則返回-1 #include<grp.h> //on linux #include<unistd.h> //on FreeBSD,Mac OS X,and Solaris int setgroups(int ngroups,const gid_t grouplist[]); #include<grp.h> //on linux and solaris #include<unistd.h> //on freebsd and mac os x int initgroups(const char *username,gid_t basegid); //兩個函數返回值:若成功則返回0,若出錯則返回-1
getgroups將各附加組ID添些到數組grouplist中,該數組中存放的元素最多爲gidsetsize個。實際填寫到數組中的附加組ID數由函數返回。
做爲一個特例,如若gidsetsize爲0,則函數值返回附加組ID數,而對數組grouplist則不做修改(這使調用者能夠肯定grouplist數組的長度,以便進行分配)
setgroups可由超級用戶調用以便爲調用進程設置附加組ID表。grouplist是組ID數組,而ngroups指定了數組中的元素個數,ngroups的值不能大於NGROUPS_MAX
6.6 實現的區別
在FreeBSD中,陰影口令文件是/etc/master.passwd,可使用特殊命令編輯該文件,它反過來會從陰影文件愛你產生/etc /passwd的一個副本。另外,還會產生該文件的散列版本。/etc/pwd.db是/etc/passwd的散列版本,/etc/spwd.db是 /etc/master.passwd的散列版本。這些爲大型系統提供了更好的性能。
可是Mac OS X只以單用戶模式使用/etc/passwd和/etc/master.passwd。在維護系統時,單用戶模式一般意味着不能提供任何系統服務。正常運行期間的多用戶方式即netinfo目錄服務提供對用戶和組帳戶信息的訪問。
雖 然Linux和Solaris支持相似的陰影口令接口,但二者之間存在某些微妙的區別。例如,gr_uid在Solaris中定義爲int類型,在 Linux中則定義爲long int。另外一個區別是帳戶不活動字段。Solaris將其定義爲用戶上次登陸依賴所通過的天數,而Linux則將其定義爲到口令過時的尚餘天數。
在 不少系統中,用戶和組數據庫是用網絡信息服務(Network Information Service,NIS)實現的。這使管理員可編輯數據庫的主副本,而後將它自動分發到組織中的全部服務器上。客戶端系統能夠聯繫服務器以查看用戶和組的 有關嘻嘻。NIS+和輕量級目錄訪問協議(Lightweight Directory Access Protocal,LDAP)提供了相似功能。不少系統經過配置文件/etc/nsswitch.conf來控制管理每一類信息的方法。
6.7 其餘數據文件
除口令文件和組文件外,UNIX系統還使用不少其餘文件。例如,BSD網絡軟件有一個記錄各網絡服務器所提供服務的數據文件(/etc/services)
通常狀況下,對於每一個數據文件至少有三個函數:
(1)get函數:讀下一個記錄,若是須要,還可打開該文件。
(2)set函數:打開相應數據文件(若是還沒有打開),而後反饒該文件。
(3)end函數:關閉相應數據文件
另外,若是數據文件支持某種形式的關鍵字搜索,則會提供搜索具備指定關鍵字記錄的例程
6.8 登錄帳戶信息
大多數UNIX系統都提供下列兩個數據文件:utmp文件,它記錄當前登錄進系統的各個用戶,wtmp文件,它跟蹤各個登錄和註銷時間。
struct utmp{ char ut_line[8]; char ut_name[8]; long ut_time; };
登錄時,login程序添些此類型結構,而後將其寫入到utmp文件中,同時也將其填寫到wtmp文件中。註銷時,init進程將utmp文件中相應的記錄擦除(每一個字節都填以0),並將一個新紀錄填寫到wtmp文件中。在wtmp文件的註銷記錄中,將ut_name字段清零。在系統從新啓動時,以及更改系統時間和日期的先後,都在wtmp文件中添些特殊的記錄項。
6.9 系統標識
POSIX.1定義了uname函數,它返回與當前主機和操做系統有關的信息
#include<sys/utsname.h> int uname(struct utsname *name); //返回值:若成功則返回非負值,若出錯則返回-1
經過該函數的參數想起傳遞一個utsname結構的地址,而後該函數填寫此結構
struct utsname{ char sysname[]; char nodename[]; char release[]; char version[]; char machine[]; };
6.10 時間和日期例程
time函數返回當前時間和日期
#include<time.h> time_h time(time_t *calptr); //返回值:若成功則返回時間值,若出錯則返回-1
與time函數相比,gettimeofday提供了更高的分辨率(最高爲微秒級)。這對某些應用很重要
#include<sys/time.h> int gettimeofday(struct timeval *restrict tp,void *restrict tzp); 返回值:老是返回0
gettimeofday函數將當前時間存放在tp指向timeval結構中,而該結構存儲秒和微秒。
struct timeval{
time_t tv_sec;
long tv_usec;
}
localtime和gmtime將日曆時間轉換。
#include<time.h> struct tm *gmtime(const time *calptr); struct tm *localtime(const time_t *calptr); //兩個函數返回值:指向tm結構的指針
localtime和gmtime之間的區別是:localtime將日曆時間轉換成本地時間,而gmtime則將日曆時間轉換稱國際標準時間的年、月、、、
函數mktime以本地時間做爲參數,將其轉換稱time_t
#include<time.h>
time_t mktime(struct tm *tmptr);
返回值:若成功則返回日曆時間,若出錯則返回-1
此外還有asctime,ctime,,mktime,strftime