最新消息:

Linux 双因子认证(密码+PIN)C语言版

IT技术 ipcpu 5923浏览

Linux 双因子认证(密码+PIN)C语言版.md

关键字

Linux C语言 PAM SSH 2 Two Multi Factor Authentication Login 双因子 多因子 密保 TOKEN 一次性口令 PASSPOD OTP yubikey 认证 安全 登录

实现效果

最简单的实现的方式,用户SSH登录时需要输入用户名+PIN+密码方式才能登录。
这里的PIN是一个字符串,例如”ipcpu.com”或者电话号码6192*,固定死的,不会变,所有用户共享。

  1. [root@control.ipcpu.com ~]# ssh ipcpu@211.81.175.101
  2. PIN: 6192***
  3. Password:
  4. [ipcpu@s18.ipcpu.com ~]$
  5. [ipcpu@s18.ipcpu.com ~]$
  6. [ipcpu@s18.ipcpu.com ~]$id
  7. uid=501(ipcpu) gid=501(ipcpu) groups=501(ipcpu)

实现代码(C语言)

  1. /*******************************************************************************
  2. * file: 2ndfactor.c
  3. * author: www.ipcpu.com
  4. * description: PAM module to provide 2 factor authentication
  5. *******************************************************************************/
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <curl/curl.h>
  10. #include <security/pam_appl.h>
  11. #include <security/pam_modules.h>
  12. /* expected hook */
  13. PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
  14. return PAM_SUCCESS ;
  15. }
  16. /* this function is ripped from pam_unix/support.c, it lets us do IO via PAM */
  17. int converse( pam_handle_t *pamh, int nargs, struct pam_message **message, struct pam_response **response ) {
  18. int retval ;
  19. struct pam_conv *conv ;
  20. retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
  21. if( retval==PAM_SUCCESS ) {
  22. retval = conv->conv( nargs, (const struct pam_message **) message, response, conv->appdata_ptr ) ;
  23. }
  24. return retval ;
  25. }
  26. /* expected hook, this is where custom stuff happens */
  27. PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
  28. int retval ;
  29. int i ;
  30. /* these guys will be used by converse() */
  31. char *input ;
  32. char *pin="6192***" ;
  33. struct pam_message msg[1],*pm sg[1];
  34. struct pam_response *resp;
  35. /* getting the username that was used in the previous authentication */
  36. const char *username ;
  37. if( (retval = pam_get_user(pamh,&username,"login: "))!=PAM_SUCCESS ) {
  38. return retval ;
  39. }
  40. /*ak47@ipcpu.com,code start here!*/
  41. /* setting up conversation call prompting for one-time code */
  42. pmsg[0] = &msg[0] ;
  43. msg[0].msg_style = PAM_PROMPT_ECHO_ON ;
  44. msg[0].msg = "PIN: " ;
  45. /*variable resp used to recive keyboard input*/
  46. resp = NULL ;
  47. if( (retval = converse(pamh, 1 , pmsg, &resp))!=PAM_SUCCESS ) {
  48. // if this function fails, make sure that ChallengeResponseAuthentication in sshd_config is set to yes
  49. return retval ;
  50. }
  51. /* retrieving user input,give PIN to variable input */
  52. if( resp ) {
  53. if( (flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL ) {
  54. free( resp );
  55. return PAM_AUTH_ERR;
  56. }
  57. input = resp[ 0 ].resp;
  58. resp[ 0 ].resp = NULL;
  59. } else {
  60. return PAM_CONV_ERR;
  61. }
  62. /* comparing user input with known code */
  63. if( strcmp(input, pin)==0 ) {
  64. /* good to go! */
  65. free( input ) ;
  66. return PAM_SUCCESS ;
  67. } else {
  68. /* wrong pin */
  69. free( input ) ;
  70. return PAM_AUTH_ERR ;
  71. }
  72. /* we shouldn't read this point, but if we do, we might as well return something bad */
  73. return PAM_AUTH_ERR ;
  74. }

编译方法:

  1. #@编译之前需要安装pam-devel包,否则会报错
  2. gcc -fPIC -lcurl -c 2ndfactor.c
  3. ld -x --shared -o /lib/security/2ndfactor.so 2ndfactor.o

将其配置到/etc/pam.d/sshd,SSH配置中打开ChallengeResponseAuthentication

  1. [root@IPCPU-11 ~]# head /etc/pam.d/sshd
  2. #%PAM-1.0
  3. auth requisite 2ndfactor.so
  4. auth required pam_sepermit.so
  5. auth include password-auth
  6. account required pam_nologin.so
  7. account include password-auth
  8. password include password-auth
  9. # pam_selinux.so close should be the first session rule
  10. session required pam_selinux.so close
  11. session required pam_loginuid.so
  12. [root@IPCPU-11 ~]#

进阶-每个用户使用独立的PIN

使用固定的PIN优点太low了,接下来我们介绍进阶的办法,每个人用自己的PIN。
首先PIN需要有个地方存放起来,我们就直接使用/etc/passwd的comment字段来存储。
可以通过命令 usermod来修改。如下,

  1. [root@IPCPU-11 ~]# usermod -c 6192*** root
  2. [root@IPCPU-11 ~]# cat /etc/passwd
  3. root:x:0:0:6192***:/root:/bin/bash

PAM模块代码

  1. /*******************************************************************************
  2. * file: pin.c
  3. * author: www.ipcpu.com
  4. * description: PAM module to provide 2 factor authentication
  5. *******************************************************************************/
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <pwd.h>
  9. #include <string.h>
  10. #include <syslog.h>
  11. #include <limits.h>
  12. #include <security/pam_appl.h>
  13. #include <security/pam_modules.h>
  14. //#include <curl/curl.h>
  15. /* expected hook */
  16. PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
  17. return PAM_SUCCESS ;
  18. }
  19. /* this function is ripped from pam_unix/support.c, it lets us do IO via PAM */
  20. int converse( pam_handle_t *pamh, int nargs, struct pam_message **message, struct pam_response **response ) {
  21. int retval ;
  22. struct pam_conv *conv ;
  23. retval = pam_get_item( pamh, PAM_CONV, (const void **) &conv ) ;
  24. if( retval==PAM_SUCCESS ) {
  25. retval = conv->conv( nargs, (const struct pam_message **) message, response, conv->appdata_ptr ) ;
  26. }
  27. return retval ;
  28. }
  29. /* expected hook, this is where custom stuff happens */
  30. PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
  31. int retval ;
  32. int i ;
  33. /* these guys will be used by converse() */
  34. char *input ;
  35. struct pam_message msg[1],*pmsg[1];
  36. struct pam_response *resp;
  37. /* getting the username that was used in the previous authentication */
  38. const char *username ;
  39. if( (retval = pam_get_user(pamh,&username,"login: "))!=PAM_SUCCESS ) {
  40. return retval ;
  41. }
  42. /* get user comment from /etc/passwd */
  43. struct passwd *pwd;
  44. struct passwd pwdbuf;
  45. char pwbuffer[2 * PATH_MAX];
  46. char *gecos ;
  47. if( (retval = getpwnam_r(username, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd))!=PAM_SUCCESS || NULL == pwd ) {
  48. //syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:could not get comment." );
  49. //gecos = "jshjdkshakg";
  50. //syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:gecos:%s.", gecos );
  51. //return retval ;
  52. return PAM_AUTH_ERR;
  53. } else {
  54. gecos = pwd->pw_gecos;
  55. //syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:get comment ok." );
  56. //syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:gecos:%s.", gecos );
  57. }
  58. /* setting up conversation call prompting for one-time code */
  59. pmsg[0] = &msg[0] ;
  60. msg[0].msg_style = PAM_PROMPT_ECHO_OFF ;
  61. msg[0].msg = "PIN: " ;
  62. /*variable resp used to recive keyboard input*/
  63. resp = NULL ;
  64. if( (retval = converse(pamh, 1 , pmsg, &resp))!=PAM_SUCCESS ) {
  65. // if this function fails, make sure that ChallengeResponseAuthentication in sshd_config is set to yes
  66. return retval ;
  67. }
  68. /* retrieving user input,give PIN to variable input */
  69. if( resp ) {
  70. if( (flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL ) {
  71. free( resp );
  72. return PAM_AUTH_ERR;
  73. }
  74. input = resp[ 0 ].resp;
  75. resp[ 0 ].resp = NULL;
  76. } else {
  77. return PAM_CONV_ERR;
  78. }
  79. /* comparing user input with known code */
  80. if( strcmp(input, gecos)==0 ) {
  81. /* right to go! */
  82. free( input ) ;
  83. syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:success.%s:%s.", username,gecos );
  84. return PAM_SUCCESS ;
  85. } else {
  86. /* wrong pin !*/
  87. free( input ) ;
  88. syslog(LOG_ERR|LOG_AUTHPRIV, "PIN-PAM:failed.%s:%s.", username,gecos );
  89. return PAM_AUTH_ERR ;
  90. }
  91. /* we shouldn't read this point, but if we do, we might as well return something bad */
  92. return PAM_AUTH_ERR ;
  93. }

参考文章

http://ben.akrin.com/?p=1068

转载请注明:IPCPU-网络之路 » Linux 双因子认证(密码+PIN)C语言版