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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
/* expected hook */
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
return PAM_SUCCESS ;
}
/* this function is ripped from pam_unix/support.c, it lets us do IO via PAM */
int converse( pam_handle_t *pamh, int nargs, struct pam_message **message, struct pam_response **response ) {
int retval ;
struct pam_conv *conv ;
retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
if( retval==PAM_SUCCESS ) {
retval = conv->conv( nargs, (const struct pam_message **) message, response, conv->appdata_ptr ) ;
}
return retval ;
}
/* expected hook, this is where custom stuff happens */
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
int retval ;
int i ;
/* these guys will be used by converse() */
char *input ;
char *pin="6192***" ;
struct pam_message msg[1],*pm sg[1];
struct pam_response *resp;
/* getting the username that was used in the previous authentication */
const char *username ;
if( (retval = pam_get_user(pamh,&username,"login: "))!=PAM_SUCCESS ) {
return retval ;
}
/*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
return retval ;
}
/* retrieving user input,give PIN to variable input */
if( resp ) {
if( (flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL ) {
free( resp );
return PAM_AUTH_ERR;
}
input = resp[ 0 ].resp;
resp[ 0 ].resp = NULL;
} else {
return PAM_CONV_ERR;
}
/* comparing user input with known code */
if( strcmp(input, pin)==0 ) {
/* good to go! */
free( input ) ;
return PAM_SUCCESS ;
} else {
/* wrong pin */free( input ) ;
return PAM_AUTH_ERR ;
}
/* we shouldn't read this point, but if we do, we might as well return something bad */
return PAM_AUTH_ERR ;
}
编译方法:
#@编译之前需要安装pam-devel包,否则会报错
gcc -fPIC -lcurl -c 2ndfactor.c
ld -x --shared -o /lib/security/2ndfactor.so 2ndfactor.o
将其配置到/etc/pam.d/sshd,SSH配置中打开ChallengeResponseAuthentication
[root@IPCPU-11 ~]# head /etc/pam.d/sshd
#%PAM-1.0
auth requisite 2ndfactor.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 -c 6192*** 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 <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <string.h>
#include <syslog.h>
#include <limits.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
//#include <curl/curl.h>
/* expected hook */
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
return PAM_SUCCESS ;
}
/* this function is ripped from pam_unix/support.c, it lets us do IO via PAM */
int converse( pam_handle_t *pamh, int nargs, struct pam_message **message, struct pam_response **response ) {
int retval ;
struct pam_conv *conv ;
retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
if( retval==PAM_SUCCESS ) {
retval = conv->conv( nargs, (const struct pam_message **) message, response, conv->appdata_ptr ) ;
}
return retval ;
}
/* expected hook, this is where custom stuff happens */
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
int retval ;
int i ;
/* these guys will be used by converse() */
char *input ;
struct pam_message msg[1],*pmsg[1];
struct pam_response *resp;
/* getting the username that was used in the previous authentication */
const char *username ;
if( (retval = pam_get_user(pamh,&username,"login: "))!=PAM_SUCCESS ) {
return retval ;
}
/* get user comment from /etc/passwd */
struct passwd *pwd;
struct passwd pwdbuf;
char pwbuffer[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 ;
return PAM_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
return retval ;
}
/* retrieving user input,give PIN to variable input */
if( resp ) {
if( (flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL ) {
free( resp );
return PAM_AUTH_ERR;
}
input = resp[ 0 ].resp;
resp[ 0 ].resp = NULL;
} else {
return PAM_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 );
return PAM_SUCCESS ;
} else {
/* wrong pin !*/
free( input ) ;
syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:failed.%s:%s.", username,gecos );
return PAM_AUTH_ERR ;
}
/* we shouldn't read this point, but if we do, we might as well return something bad */
return PAM_AUTH_ERR ;
}
参考文章
转载请注明:IPCPU-网络之路 » Linux 双因子认证(密码+PIN)C语言版