新增支援 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. |
新聞熱點
疑難解答