新增支援shadow程式事實上是很直接的。唯一的問題是程式需要以root(或SUIDroot)權限執行,這樣才可以存取/etc/shadow檔。 這顯示一個大問題:當建立SUID程式時需要很小心依照程式運作。舉例說明:如果以個程式有shellescape,如果程式本身是SUIDroot將不需要
新增支援 shadow 程式事實上是很直接的。 唯一的問題是程式需要以 root (或 SUID root) 權限執行,這樣才可以存取 /etc/shadow 檔。
這顯示一個大問題: 當建立 SUID 程式時需要很小心依照程式運作。舉例說明: 如果以個程式有 shell escape,如果程式本身是 SUID root 將不需要以 root 方式呈現。
對程式新增支援 shadow 而言,它可以檢查密碼,但不需以 root 權限執行,而是以 SUID shadow 取代執行比較
安全。 xlock 程式就是一個例子。
接下來□例介紹, pppd-1.2.1d 已經以 SUID as root 方式執行,所以新增 shadow 支援應該不會使程式產生任何影響。
8.1 標頭檔(Header files)
標頭檔應存在 /usr/include/shadow。 應該有一個 /usr/include/shadow.h檔,但是它將 symbolic link 到 /usr/include/shadow/shadow.h。
為了新增支援 shadow 程式,你需要 include 標頭檔:
#include
#include
8.2 libshadow.a 函式庫(library)
當你安裝 Shadow Suite, libshadow.a 檔被建立和安裝在 /usr/lib 目錄。
當編譯一個 shadow support 程式,linker 需要包括 libshadow.a 函式庫進入鏈結。
執行如下:
gcc program.c -o program -lshadow
然而,就像我們接下來要看的例子,大部分大程式使用 Makefile 且通常有變數呼叫 LIBS=... 需要被修改。
8.3 Shadow 結構(Structure)
libshadow.a 函式庫對它從 /etc/shadow 檔接收資訊使用結構化呼叫。這是從 /usr/include/shadow/shadow.h 標頭檔的 spwd 結構定義:
--------------------------------------------------------------------------------
struct spwd
{
char *sp_namp; /* login name */
char *sp_pwdp; /* encrypted password */
sptime sp_lstchg; /* date of last change */
sptime sp_min; /* minimum number of days between changes */
sptime sp_max; /* maximum number of days between changes */
sptime sp_warn; /* number of days of warning before password
expires */
sptime sp_inact; /* number of days after password expires
until the account becomes unusable. */
sptime sp_expire; /* days since 1/1/70 until account expires
*/
unsigned long sp_flag; /* reserved for future use */
};
--------------------------------------------------------------------------------
Shadow Suite 可以放除了編碼密碼之外的資料到 sp_pwdp 欄位。密碼欄位可包括:
username:Npge08pfz4wuk;@/sbin/extra:9479:0:10000::::
這表示一個額外的密碼, /sbin/extra 程式應該被更多的權限呼叫。 程式的呼叫需取得使用者名稱和指出為何需被呼叫的 switch才可通過。 查看 /usr/include/shadow/pwauth.h 和原始碼 pwauth.c 獲得更多資訊。
為何我們應使用 pwauth 去表示真正的權限,這是什麽意思,它將使第二組權限也跑得很好。
Shadow Suite 作者指出因為大部分存在的程式都不這麽作羅,所以 Shadow Suite未來的版本將移除。
8.4 Shadow 函式(Functions)
shadow.h 包含 libshadow.a 函式庫:
--------------------------------------------------------------------------------
extern void setspent __P ((void));
extern void endspent __P ((void));
extern struct spwd *sgetspent __P ((__const char *__string));
extern struct spwd *fgetspent __P ((FILE *__fp));
extern struct spwd *getspent __P ((void));
extern struct spwd *getspnam __P ((__const char *__name));
extern int putspent __P ((__const struct spwd *__sp, FILE *__fp));
--------------------------------------------------------------------------------
我們將使用的□例程式是: getspnam 將對供應名稱恢復對我們 spwd 結構。
8.5 □例
這是一個□例描述新增 shadow 支援程式,但預設值並沒有。
本□例使用 Point-to-Point Protocol Server (pppd-1.2.1d),它有個模式是表示 從 /etc/passwd 檔取代 PAP 或 CHAP 檔使用帳號密碼的 PAP 權限,你將不需要在 pppd-2.2.0 加這些程式碼,因為它已經存在羅。
pppd 的未來大致上不會被使用很多,但是如果你安裝 Shadow Suite,儲存在 /etc/passwd 檔的密碼將無法運作。
在 pppd-1.2.1d 權限使用的程式碼是位在 /usr/src/pppd-1.2.1d/pppd/auth.c 檔。
接下來程式碼需要被加在所有其他 #include 指令檔案的最上頭,我們將注意有環境指令的 #includes。
--------------------------------------------------------------------------------
#ifdef HAS_SHADOW
#include
#include
#endif
--------------------------------------------------------------------------------
接下來要做的事情是變更實際碼, 我們將變更 auth.c 檔。
變更前 auth.c 檔 function 為:
--------------------------------------------------------------------------------
/*
* login - Check the user name and password against the system
* password database, and login the user if OK.
*
* returns:
* UPAP_AUTHNAK: Login failed.
* UPAP_AUTHACK: Login succeeded.
* In either case, msg points to an appropriate message.
*/
static int
login(user, passwd, msg, msglen)
char *user;
char *passwd;
char **msg;
int *msglen;
{
struct passwd *pw;
char *epasswd;
char *tty;
if ((pw = getpwnam(user)) == NULL) {
return (UPAP_AUTHNAK);
}
/*
* XXX If no passwd, let them login without one.
*/
if (pw->pw_passwd == '\0') {
return (UPAP_AUTHACK);
}
epasswd = crypt(passwd, pw->pw_passwd);
if (strcmp(epasswd, pw->pw_passwd)) {
return (UPAP_AUTHNAK);
}
syslog(LOG_INFO, "user %s logged in", user);
/*
* Write a wtmp entry for this user.
*/
tty = strrchr(devname, '/');
if (tty == NULL)
tty = devname;
else
tty++;
logwtmp(tty, user, ""); /* Add wtmp login entry */
logged_in = TRUE;
return (UPAP_AUTHACK);
}
--------------------------------------------------------------------------------
使用者的密碼被放在 pw->pw_passwd,所以我們需新增 getspnam function,這將會把密碼放到 spwd->sp_pwdp。
我們將新增 pwauth function 來表示真正的權限。 這將在 shadow 檔設定時自動產生第二個權限。
變更為可以支援 shadow 後的 auth.c function:
--------------------------------------------------------------------------------
/*
* login - Check the user name and password against the system
* password database, and login the user if OK.
*
* This function has been modified to support the Linux Shadow Password
* Suite if USE_SHADOW is defined.
*
* returns:
* UPAP_AUTHNAK: Login failed.
* UPAP_AUTHACK: Login succeeded.
* In either case, msg points to an appropriate message.
*/
static int
login(user, passwd, msg, msglen)
char *user;
char *passwd;
char **msg;
int *msglen;
{
struct passwd *pw;
char *epasswd;
char *tty;
#ifdef USE_SHADOW
struct spwd *spwd;
struct spwd *getspnam();
#endif
if ((pw = getpwnam(user)) == NULL) {
return (UPAP_AUTHNAK);
}
#ifdef USE_SHADOW
spwd = getspnam(user);
if (spwd)
pw->pw_passwd = spwd->sp-pwdp;
#endif
/*
* XXX If no passwd, let NOT them login without one.
*/
if (pw->pw_passwd == '\0') {
return (UPAP_AUTHNAK);
}
#ifdef HAS_SHADOW
if ((pw->pw_passwd && pw->pw_passwd[0] == '@'
&& pw_auth (pw->pw_passwd+1, pw->pw_name, PW_LOGIN, NULL))
|| !valid (passwd, pw)) {
return (UPAP_AUTHNAK);
}
#else
epasswd = crypt(passwd, pw->pw_passwd);
if (strcmp(epasswd, pw->pw_passwd)) {
return (UPAP_AUTHNAK);
}
#endif
syslog(LOG_INFO, "user %s logged in", user);
/*
* Write a wtmp entry for this user.
*/
tty = strrchr(devname, '/');
if (tty == NULL)
tty = devname;
else
tty++;
logwtmp(tty, user, ""); /* Add wtmp login entry */
logged_in = TRUE;
return (UPAP_AUTHACK);
}
--------------------------------------------------------------------------------
嚴謹的□例將啟發我們在作其他改變的幫助。 原始的版本如果在 /etc/passwd 檔沒有任何密碼,可允許存取傳回的 UPAP_AUTHACK 。這是不好的,因為簽入的使用是使用一個允許存取 PPP process的帳號,然後檢查帳號密碼,該帳號密碼是由 RAP 、在 /etc/passwd 檔的帳號和 /etc/shadow 檔的密碼供應。
所以如果我們設定原本版本對每個使用者,如 ppp 可以在 shell 執行,然後任何人可以獲得 ppp 鏈結透過設定他們對使用者 ppp 的 PAP 和 null 的密碼。
我們修正 UPAP_AUTHNAK 取代 UPAP_AUTHACK 如果密碼欄位是空的。
有趣的是 pppd-2.2.0 有相同的問題。
接下來我們需要變更 Makefile 以便讓兩件事發生:
USE_SHADOW 必須被重新定義且libshadow.a 需要被新增到鏈結 process。
編輯 Makefile 且新增:
LIBS = -lshadow
然後我們找到這一行:
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t
然後改變它變成:
COMPILE_FLAGS = -I.. -D_linux_=1 -DGIDSET_TYPE=gid_t -DUSE_SHADOW
現在執行 make 跟 install.