六、Linux 操作系統安全登陸設計
自己編寫PAM 模塊並編譯成動態鏈接庫so 文件,將其添加進/etc/pam.d/login 文件
中實現命令行安全登陸設計,將其添加進/etc/pam.d/lightdm 文件中實現圖形界面的安全
登陸。安全登陸的密碼根據時間戳的復雜組合,使登錄密碼每十秒更改一次。用戶需要在
安全主機上安裝相同加密算法的程序,獲取實時更改的密鑰以登錄Linux 操作系統。本
章將重點講解Linux 操作系統安全登陸設計,包括程序設計以及實時密鑰設計。
6.1 Linux-PAM 配置文件
PAM 的各個模塊一般存放在/lib/security/ 或/lib64/security/ 中,以動態庫文件的
形式存在(可參閱dlopen(3)),文件名格式一般為pam_*.so。PAM 的配置文件可以是
/etc/pam.conf 這一個文件,也可以是/etc/pam.d/ 文件夾內的多個文件。如果/etc/pam.d/
這個文件夾存在,Linux-PAM 將自動忽略/etc/pam.conf。
/etc/pam.conf 類型的格式如下:
服務名稱工作類別控制模式模塊路徑模塊參數
/etc/pam.d/ 類型的配置文件通常以每一個使用PAM 的程序的名稱來命令。比如
/etc/pam.d/su,/etc/pam.d/login 等等。還有些配置文件比較通用,經常被別的配置文件引
用,也放在這個文件夾下,比如/etc/pam.d/system-auth。這些文件的格式都保持一致:
工作類別控制模式模塊路徑模塊參數
不難看出,文件夾形式的配置文件中只是沒有了服務名稱這一列:服務名稱已經是文
件名了。
內容如下
# # The PAM configuration file for the Shadow `login' service # # Enforce a minimal delay in case of failure (in microseconds). # (Replaces the `FAIL_DELAY' setting from login.defs) # Note that other modules may require another minimal delay. (for example, # to disable any delay, you should add the nodelay option to pam_unix) auth optional pam_faildelay.so delay=3000000 # Outputs an issue file prior to each login prompt (Replaces the # ISSUE_FILE option from login.defs). Uncomment for use # auth required pam_issue.so issue=/etc/issue # Disallows root logins except on tty's listed in /etc/securetty # (Replaces the `CONSOLE' setting from login.defs) # # With the default control of this module: # [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] # root will not be prompted for a password on insecure lines. # if an invalid username is entered, a password is prompted (but login # will eventually be rejected) # # You can change it to a "requisite" module if you think root may mis-type # her login and should not be prompted for a password in that case. But # this will leave the system as vulnerable to user enumeration attacks. # # You can change it to a "required" module if you think it permits to # guess valid user names of your system (invalid user names are considered # as possibly being root on insecure lines), but root passwords may be # communicated over insecure lines. auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so # Disallows other than root logins when /etc/nologin exists # (Replaces the `NOLOGINS_FILE' option from login.defs) auth requisite pam_nologin.so # SELinux needs to be the first session rule. This ensures that any # lingering context has been cleared. Without this it is possible # that a module could execute code in the wrong domain. # When the module is present, "required" would be sufficient (When SELinux # is disabled, this returns success.) session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close # Sets the loginuid process attribute session required pam_loginuid.so # SELinux needs to intervene at login time to ensure that the process # starts in the proper default security context. Only sessions which are # intended to run in the user's context should be run after this. session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open # When the module is present, "required" would be sufficient (When SELinux # is disabled, this returns success.) # This module parses environment configuration file(s) # and also allows you to use an extended config # file /etc/security/pam_env.conf. # # parsing /etc/environment needs "readenv=1" session required pam_env.so readenv=1 # locale variables are also kept into /etc/default/locale in etch # reading this file *in addition to /etc/environment* does not hurt session required pam_env.so readenv=1 envfile=/etc/default/locale # Standard Un*x authentication. @include common-auth # This allows certain extra groups to be granted to a user # based on things like time of day, tty, service, and user. # Please edit /etc/security/group.conf to fit your needs # (Replaces the `CONSOLE_GROUPS' option in login.defs) auth optional pam_group.so # Uncomment and edit /etc/security/time.conf if you need to set # time restraint on logins. # (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs # as well as /etc/porttime) # account requisite pam_time.so # Uncomment and edit /etc/security/access.conf if you need to # set access limits. # (Replaces /etc/login.access file) # account required pam_access.so # Sets up user limits according to /etc/security/limits.conf # (Replaces the use of /etc/limits in old login) session required pam_limits.so # Prints the last login info upon successful login # (Replaces the `LASTLOG_ENAB' option from login.defs) session optional pam_lastlog.so # Prints the message of the day upon successful login. # (Replaces the `MOTD_FILE' option in login.defs) # This includes a dynamically generated part from /run/motd.dynamic # and a static (admin-editable) part from /etc/motd. session optional pam_motd.so motd=/run/motd.dynamic session optional pam_motd.so noupdate # Prints the status of the user's mailbox upon successful login # (Replaces the `MAIL_CHECK_ENAB' option from login.defs). # # This also defines the MAIL environment variable # However, userdel also needs MAIL_DIR and MAIL_FILE variables # in /etc/login.defs to make sure that removing a user # also removes the user's mail spool file. # See comments in /etc/login.defs session optional pam_mail.so standard # Create a new session keyring. session optional pam_keyinit.so force revoke # Standard Un*x account and session @include common-account @include common-session @include common-password
每一行代表一條規則。但也可以用\ 來放在行末,來連接該行和下一行。“關鍵字”
模式下,有以下幾種控制模式:
required:如果本條目沒有被滿足,那最終本次認證一定失敗,但認證過程不因此打
斷。整個棧運行完畢之后才會返回(已經注定了的)“認證失敗”信號。
requisite:如果本條目沒有被滿足,那本次認證一定失敗,而且整個棧立即中止並返
回錯誤信號。
sufficient:如果本條目的條件被滿足,且本條目之前沒有任何required 條目失敗,
則立即返回“認證成功”信號;如果對本條目的驗證失敗,不對結果造成影響。
optional:該條目僅在整個棧中只有這一個條目時才有決定性作用,否則無論該條驗
證成功與否都和最終結果無關。
include:將其他配置文件中的流程棧包含在當前的位置,就好像將其他配置文件中的
內容復制粘貼到這里一樣。
substack:運行其他配置文件中的流程,並將整個運行結果作為該行的結果進行輸出。
該模式和include 的不同點在於認證結果的作用域:如果某個流程棧include 了一個帶
requisite 的棧,這個requisite 失敗將直接導致認證失敗,同時退出棧;而某個流程棧
substack 了同樣的棧時,requisite 的失敗只會導致這個子棧返回失敗信號,母棧並不會在
此退出。
“返回值=行為”模式則更為復雜,其格式如下:
[value1=action1 value2=action2 ...]
其中, valueN 的值是各個認證模塊執行之后的返回值。有success、user_unknown、
new_authtok_reqd、default 等等數十種。其中,default 代表其他所有沒有明確說明
的返回值。返回值結果清單可以在
/usr/include/security/_pam_types.h 中找到,也可以查詢pam(3) 獲取詳細描述。流程
棧中很可能有多個驗證規則,每條驗證的返回值可能不盡相同,那么到
底哪一個驗證規則能作為最終的結果呢?這就需要actionN 的值來決定了。
actionN 的值有以下幾種:
ignore:在一個棧中有多個認證條目的情況下,如果標記ignore 的返回值被命中,
那么這條返回值不會對最終的認證結果產生影響。
bad:標記bad 的返回值被命中時,最終的認證結果注定會失敗。此外,如果這條bad
的返回值是整個棧的第一個失敗項,那么整個棧的返回值一定是這個返回值,后面的認證
無論結果怎樣都改變不了現狀了。
die:標記die 的返回值被命中時,馬上退出棧並宣告失敗。整個返回值為這個die 的
返回值。
ok:在一個棧的運行過程中,如果ok 前面沒有返回值,或者前面的返回值為
PAM_SUCCESS,那么這個標記了ok 的返回值將覆蓋前面的返回值。但如果前面執行過
的驗證中有最終將導致失敗的返回值,那ok 標記的值將不會起作用。
done:在前面沒有bad 值被命中的情況下,done 值被命中之后將馬上被返回,並退
出整個棧。
N(一個自然數):功效和ok 類似,並且會跳過接下來的N 個驗證步驟。如果N
= 0 則和ok 完全相同。
reset:清空之前生效的返回值,並且從下面的驗證起重新開始。
我們在前文中已經介紹了控制模式(contro)的“關鍵字”模式。實際上, “關鍵字”
模式可以等效地用“返回值=行為”模式來表示。具體的對應如下:
required:
[success=ok new_authtok_reqd=ok ignore=ignore default=bad]
requisite:
[success=ok new_authtok_reqd=ok ignore=ignore default=die]
sufficient:
[success=done new_authtok_reqd=done default=ignore]
optional:
[success=ok new_authtok_reqd=ok default=ignore]
6.2 MyPAM 程序編寫
mypam.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <security/pam_appl.h> #include <security/pam_modules.h> #include <security/pam_ext.h> //編寫獲取系統時間函數: void getTime(char str[],int *y,int *m,int *d){ time_t now; struct tm *tm; now = time(0); //獲得當前系統時間 if ((tm = localtime (&now)) == NULL) { printf ("Error extracting time stuff\n"); return; } sprintf(str, "%02d%02d", tm->tm_min , tm->tm_sec); //提取分秒用於處理密碼 str[4] = '\0'; *y = tm->tm_year+1900;*m = tm->tm_mon;*d = tm->tm_mday; return; } //添加認證管理模塊,設置用戶證書: PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) { printf("Setcred\n"); return PAM_SUCCESS; } //添加賬號管理模塊: PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { printf("Acct mgmt\n"); return PAM_SUCCESS; } //添加用戶認證模塊: PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) { int retval; const char* pUsername; retval = pam_get_user(pamh, &pUsername, NULL); printf("歡迎來到我的PAM %s\n", pUsername); if (retval != PAM_SUCCESS) { printf("what?"); return retval; } printf("請輸入密碼: %s\n", pUsername); char* pPw; char * p = "Password:"; retval = pam_prompt(pamh,PAM_PROMPT_ECHO_OFF,&pPw,"%s",p); if (retval != PAM_SUCCESS) { printf("what??"); return retval; } char str[5]; int y,m,d; getTime(str,&y,&m,&d);//str=min sec xxxx printf("當前時間:%s\n",str); int t1 = str[0]-'0'; int t2 = str[1]-'0'; int t3 = str[2]-'0'; //每十秒更新一次 //int t4 = str[3] - '0';//秒針 char mypw[7]; char config[7]; sprintf(config,"%s","zhaoyu"); for(int i=0;i<6;i++){ int n = config[i]; mypw[i] = ((t1*t2 + t3)*n + (t3*y + t1*m + t2*d)) % 26 + 'a';//合成字符串 } mypw[6] = '\0'; printf("當前系統時間下對應密碼%s\n", mypw); if(pPw[6]!='\0'){ return PAM_CONV_ERR; } for(int i=0;i<6;i++){ if(mypw[i]!=pPw[i]) { return PAM_CONV_ERR; } } printf(" 您輸入的密碼%s\n", pPw); printf("恭喜您登陸成功!!!!!\n\n"); return PAM_SUCCESS; }
安裝依賴庫 sudo apt-get install libpam0g-dev
6.3 編譯MyPAM.c 文件
命令行輸入
gcc -fPIC -fno-stack-protector -c mypam.c
完成編譯並生成mypam.o 文件
6.4 生成動態鏈接庫so 文件
命令行輸入
sudo ld -x --shared -o /lib/x86_64-linux-gnu/security/mypam.so mypam.o
生成的文件放於/lib/x86_64-linux-gnu/security/目錄下,以便login 文件和
lightdm 文件使用
生產了mypam.so 文件
6.5 編寫login 和lightdm 登錄文件
若想更改登錄方式,命令行登陸必須修改login 文件,圖形登陸必須修改
lightdm 文件。
在這兩個文件中最開始添加一段命令:
auto sufficient mypam.so
這樣登陸時就可以執行我們編譯的動態鏈接庫,進而執行我們自己編寫
mypam.c 文件進行動態密碼登錄。
七、Linux 操作系統安全登陸實現
7.1 必要工具下載
我們需要一下必要工具
pam-dev pam 驅動
vim 編輯器
PAM 驅動下載
sudo apt-get install libpam0g-dev //前面步驟已完成,PAM 驅動下載 sudo apt-cache search pam //查詢是否pam 包 apt-cache search pam-dev //查詢是否有pam 驅動
7.2 使用vim 編輯器編寫pam 程序
使用上一章的mypam.c程序
7.3 修改login 和lightdm 登錄文件
生成login 登陸文件副本
sudo cp /etc/pam.d/login /etc/pam.d/login.bk
編輯登錄文件
在這兩個文件中最開始添加一段命令:
auto sufficient mypam.so(若第6部分做了,則不用做)
保存並退出
創建圖形登陸界面lightdm 副本
需要注意的是,自Ubuntu17 開始,使用gdm代替lightdbm,所以ubuntu18.04默認是沒有lightdbm的
安裝lightdm模塊
sudo apt-get install lightdm
使用 sudo dpkg-reconfigure <gdm3/lightdm> 切換
創建圖形登陸界面lightdm 副本
sudo cp /etc/pam.d/lightdm /etc/pam.d/lightdm.bk
在第二行加入 auth sufficient mypam.so
7.4編寫PC 密鑰程序
在window下運行實時密鑰生成程序,之后再在虛擬機的ubuntu18.04上做:
password.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <Windows.h> //編寫獲取系統時間函數: void getTime(char str[],int *y,int *m,int *d){ time_t now; struct tm *tm; now = time(0); //獲得當前系統時間 if ((tm = localtime (&now)) == NULL) { printf ("Error extracting time stuff\n"); return; } sprintf(str, "%02d%02d", tm->tm_min , tm->tm_sec); //提取分秒用於處理密碼 str[4] = '\0'; *y = tm->tm_year+1900;*m = tm->tm_mon;*d = tm->tm_mday; return; } void main() { //char config[7]; //sprintf(config,"%s","cszGGG"); //config="cszGGG" char str[5]; int y,m,d,sec=0,lsec=0; while(1) { Sleep(1000); //程序延時1s getTime(str,&y,&m,&d); printf("當前時間:%s\n",str); //輸出當前時間 int t1 = str[0]-'0'; int t2 = str[1]-'0'; int t3 = str[2]-'0'; //每十秒更新一次 sec=str[3]-'0'; lsec=sec; char mypw[7]; char config[7]; sprintf(config,"%s","zhaoyu"); //config="zhaoyu" for(int i=0;i<6;i++) { int n = config[i]; mypw[i] = ((t1*t2 + t3)*n + (t3*y + t1*m + t2*d)) % 26 + 'a';//合成字符串 } mypw[6] = '\0'; printf("當前系統時間下對應密碼%s\n", mypw); } }
注意getTime和密鑰生成辦法應該與ubuntu中一致
window下運行結果如圖
八、測試Linux 安全登陸
8.1 調整系統時間
8.2 命令行登錄測試
根據windows下生成的密鑰輸入
Ubuntu18.04進入tty界面參考 https://blog.csdn.net/qq_29894613/article/details/89817259
按下Ctrl + Alt + Fn3-Fn6進入命令行虛擬終端
這里選擇Ctrl + Alt +Fn5
亂碼是由於中文字符
8.3 圖形界面登錄測試
登陸成功