Linux操作系統 可插拔認證模塊PAM(3)


六、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 圖形界面登錄測試

 

 

 

 

登陸成功

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM