六、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= # 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=
# 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= 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(); //获得当前系统时间
if ((tm = localtime (&now)) == NULL) {
printf ("Error extracting time stuff\n");
return;
}
sprintf(str, "%02d%02d", tm->tm_min , tm->tm_sec); //提取分秒用于处理密码
str[] = '\0';
*y = tm->tm_year+;*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[];
int y,m,d;
getTime(str,&y,&m,&d);//str=min sec xxxx
printf("当前时间:%s\n",str);
int t1 = str[]-'';
int t2 = str[]-'';
int t3 = str[]-''; //每十秒更新一次
//int t4 = str[3] - '0';//秒针
char mypw[];
char config[];
sprintf(config,"%s","zhaoyu");
for(int i=;i<;i++){
int n = config[i];
mypw[i] = ((t1*t2 + t3)*n + (t3*y + t1*m + t2*d)) % + 'a';//合成字符串
}
mypw[] = '\0';
printf("当前系统时间下对应密码%s\n", mypw);
if(pPw[]!='\0'){
return PAM_CONV_ERR;
}
for(int
i=;i<;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(); //获得当前系统时间
if ((tm = localtime (&now)) == NULL) {
printf ("Error extracting time stuff\n");
return;
}
sprintf(str, "%02d%02d", tm->tm_min , tm->tm_sec); //提取分秒用于处理密码
str[] = '\0';
*y = tm->tm_year+;*m = tm->tm_mon;*d = tm->tm_mday;
return;
} void main()
{
//char config[7];
//sprintf(config,"%s","cszGGG"); //config="cszGGG"
char str[];
int y,m,d,sec=,lsec=;
while()
{
Sleep(); //程序延时1s
getTime(str,&y,&m,&d);
printf("当前时间:%s\n",str); //输出当前时间
int t1 = str[]-'';
int t2 = str[]-'';
int t3 = str[]-''; //每十秒更新一次
sec=str[]-'';
lsec=sec;
char mypw[];
char config[];
sprintf(config,"%s","zhaoyu"); //config="zhaoyu"
for(int i=;i<;i++)
{
int n = config[i];
mypw[i] = ((t1*t2 + t3)*n + (t3*y + t1*m + t2*d)) % + 'a';//合成字符串
}
mypw[] = '\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 图形界面登录测试
登陆成功