Linux 双因子认证(密码+PIN)C语言版.md
关键字
Linux C语言 PAM SSH 2 Two Multi Factor Authentication Login 双因子 多因子 密保 TOKEN 一次性口令 PASSPOD OTP yubikey 认证 安全 登录
实现效果
最简单的实现的方式,用户SSH登录时需要输入用户名+PIN+密码方式才能登录。
这里的PIN是一个字符串,例如”ipcpu.com”或者电话号码6192*,固定死的,不会变,所有用户共享。
[root@control.ipcpu.com~]#ssh ipcpu@211.81.175.101
PIN:6192***
Password:
[ipcpu@s18.ipcpu.com~]$
[ipcpu@s18.ipcpu.com~]$
[ipcpu@s18.ipcpu.com~]$id
uid=501(ipcpu)gid=501(ipcpu)groups=501(ipcpu)
实现代码(C语言)
/*******************************************************************************
* file: 2ndfactor.c
* author: www.ipcpu.com
* description: PAM module to provide 2 factor authentication
*******************************************************************************/
#include
#include
#include
#include
#include
#include
/* expected hook */
PAM_EXTERNintpam_sm_setcred(pam_handle_t*pamh,intflags,intargc,constchar**argv){
returnPAM_SUCCESS;
}
/* this function is ripped from pam_unix/support.c, it lets us do IO via PAM */
intconverse(pam_handle_t*pamh,intnargs,structpam_message**message,structpam_response**response){
intretval;
structpam_conv*conv;
retval=pam_get_item(pamh,PAM_CONV,(constvoid**)&conv);
if(retval==PAM_SUCCESS){
retval=conv->conv(nargs,(conststructpam_message**)message,response,conv->appdata_ptr);
}
returnretval;
}
/* expected hook, this is where custom stuff happens */
PAM_EXTERNintpam_sm_authenticate(pam_handle_t*pamh,intflags,intargc,constchar**argv){
intretval;
inti;
/* these guys will be used by converse() */
char*input;
char*pin="6192***";
structpam_message msg[1],*pm
sg[1];
structpam_response*resp;
/* getting the username that was used in the previous authentication */
constchar*username;
if((retval=pam_get_user(pamh,&username,"login: "))!=PAM_SUCCESS){
returnretval;
}
/*ak47@ipcpu.com,code start here!*/
/* setting up conversation call prompting for one-time code */
pmsg[0]=&msg[0];
msg[0].msg_style=PAM_PROMPT_ECHO_ON;
msg[0].msg="PIN: ";
/*variable resp used to recive keyboard input*/
resp=NULL;
if((retval=converse(pamh,1,pmsg,&resp))!=PAM_SUCCESS){
// if this function fails, make sure that ChallengeResponseAuthentication in sshd_config is set to yes
returnretval;
}
/* retrieving user input,give PIN to variable input */
if(resp){
if((flags&PAM_DISALLOW_NULL_AUTHTOK)&&resp[0].resp==NULL){
free(resp);
returnPAM_AUTH_ERR;
}
input=resp[0].resp;
resp[0].resp=NULL;
}else{
returnPAM_CONV_ERR;
}
/* comparing user input with known code */
if(strcmp(input,pin)==0){
/* good to go! */
free(input);
returnPAM_SUCCESS;
}else{
/* wrong pin */
free(input);
returnPAM_AUTH_ERR;
}
/* we shouldn't read this point, but if we do, we might as well return something bad */
returnPAM_AUTH_ERR;
}
编译方法:
#@编译之前需要安装pam-devel包,否则会报错
gcc-fPIC-lcurl-c2ndfactor.c
ld-x--shared-o/lib/security/2ndfactor.so2ndfactor.o
将其配置到/etc/pam.d/sshd,SSH配置中打开ChallengeResponseAuthentication
[root@IPCPU-11~]#head/etc/pam.d/sshd
#%PAM-1.0
auth requisite2ndfactor.so
auth required pam_sepermit.so
auth include password-auth
account required pam_nologin.so
account include password-auth
password include password-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
[root@IPCPU-11~]#
进阶-每个用户使用独立的PIN
使用固定的PIN优点太low了,接下来我们介绍进阶的办法,每个人用自己的PIN。
首先PIN需要有个地方存放起来,我们就直接使用/etc/passwd的comment字段来存储。
可以通过命令 usermod来修改。如下,
[root@IPCPU-11~]#usermod-c6192***root
[root@IPCPU-11~]#cat/etc/passwd
root:x:0:0:6192***:/root:/bin/bash
PAM模块代码
/*******************************************************************************
* file: pin.c
* author: www.ipcpu.com
* description: PAM module to provide 2 factor authentication
*******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
//#include
/* expected hook */
PAM_EXTERNintpam_sm_setcred(pam_handle_t*pamh,intflags,intargc,constchar**argv){
returnPAM_SUCCESS;
}
/* this function is ripped from pam_unix/support.c, it lets us do IO via PAM */
intconverse(pam_handle_t*pamh,intnargs,structpam_message**message,structpam_response**response){
intretval;
structpam_conv*conv;
retval=pam_get_item(pamh,PAM_CONV,(constvoid**)&conv);
if(retval==PAM_SUCCESS){
retval=conv->conv(nargs,(conststructpam_message**)message,response,conv->appdata_ptr);
}
returnretval;
}
/* expected hook, this is where custom stuff happens */
PAM_EXTERNintpam_sm_authenticate(pam_handle_t*pamh,intflags,intargc,constchar**argv){
intretval;
inti;
/* these guys will be used by converse() */
char*input;
structpam_message msg[1],*pmsg[1];
structpam_response*resp;
/* getting the username that was used in the previous authentication */
constchar*username;
if((retval=pam_get_user(pamh,&username,"login: "))!=PAM_SUCCESS){
returnretval;
}
/* get user comment from /etc/passwd */
structpasswd*pwd;
structpasswd pwdbuf;
charpwbuffer[2*PATH_MAX];
char*gecos;
if((retval=getpwnam_r(username,&pwdbuf,pwbuffer,sizeof(pwbuffer),&pwd))!=PAM_SUCCESS||NULL==pwd){
//syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:could not get comment." );
//gecos = "jshjdkshakg";
//syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:gecos:%s.", gecos );
//return retval ;
returnPAM_AUTH_ERR;
}else{
gecos=pwd->pw_gecos;
//syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:get comment ok." );
//syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:gecos:%s.", gecos );
}
/* setting up conversation call prompting for one-time code */
pmsg[0]=&msg[0];
msg[0].msg_style=PAM_PROMPT_ECHO_OFF;
msg[0].msg="PIN: ";
/*variable resp used to recive keyboard input*/
resp=NULL;
if((retval=converse(pamh,1,pmsg,&resp))!=PAM_SUCCESS){
// if this function fails, make sure that ChallengeResponseAuthentication in sshd_config is set to yes
returnretval;
}
/* retrieving user input,give PIN to variable input */
if(resp){
if((flags&PAM_DISALLOW_NULL_AUTHTOK)&&resp[0].resp==NULL){
free(resp);
returnPAM_AUTH_ERR;
}
input=resp[0].resp;
resp[0].resp=NULL;
}else{
returnPAM_CONV_ERR;
}
/* comparing user input with known code */
if(strcmp(input,gecos)==0){
/* right to go! */
free(input);
syslog(LOG_ERR|LOG_AUTHPRIV,"PIN-PAM:success.%s:%s.",username,gecos);
returnPAM_SUCCESS;
}else{
/* wrong pin !*/
free(input);
syslog(LOG_ERR|LOG_AUTHPRIV,"PIN-PAM:failed.%s:%s.",username,gecos);
returnPAM_AUTH_ERR;
}
/* we shouldn't read this point, but if we do, we might as well return something bad */
returnPAM_AUTH_ERR;
}
参考文章