Loading...

0x0 前言

PAM (Pluggable Authentication Modules) 系统的一部分,它是一个用于 Linux 系统认证的模块化框架。

如果给 ssh 登录添加 TOTP 验证一般会用到 google-authenticator 模块,这个模块就是实现和使用 PAM API 实现的。

PAM 不光可以在 ssh 登录时使用在其他大多数需要验证的时候也可以使用,甚至可以在自己程序中调用,总之 PAM 是一个非常好用的系统认证框架。

本篇文章将使用 Go 实现相关功能,而 PAM 的接口是用 C 语言实现的。因此将会使用 Go 的 CGO 进行编译,并且会尽量减少 C 语言的部分。

0x1 准备

因为 PAM 是 Linux 系统的认证框架,所以在 Linux 上开发最方便

首先安装依赖库:
Debian/Ubuntu

apt install libpam0g-dev

Centos

yum install pam-devel

安装后 include 文件在 /usr/include/security/ 目录下

0x2 开发 PAM 认证模块

首先在 Go 中引入 C 的库, Go 是在 import "C" 上方使用注释的方式引入 C 的内容

注意 import "C" 上方不能有空行

#cgo LDFLAGS: -lpamCGO 的命令,LDFLAGS: -lpam 是表示链接 PAM 静态库

typedef const char cchar_t; 是将 cchar_t 自定义为 const char 关键字,因为后面讲 C 转写成 GO 的时候 const xx 无法转写,因此需要提前定义一个关键字

#include "pam_prompt_wrapper.h" 是引入一个本地自定义的库,后面会后详解

/* #cgo LDFLAGS: -lpam #include <stdio.h> #include <stdlib.h> #include <security/pam_appl.h> #include <security/pam_modules.h> #include <security/pam_ext.h> #include "pam_prompt_wrapper.h" typedef const char cchar_t; */ import "C"

函数说明

pam_sm_authenticate是认证逻辑的接口函数,也是认证的核心,函数定义在pam_modules.h

接口函数是需要开发人员自己实现的函数,在函数内可以实现各种自定义的功能

函数签名为

int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv);

使用 Go 实现,因为这个函数是要被外部调用的,因此需要在上方添加 //export 的注释,CGO 转写成 C 的时候会转成 extern 关键字

//export pam_sm_authenticate func pam_sm_authenticate(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int { return C.PAM_SUCCESS }

pam_sm_acct_mgmt是用户识别管理接口函数,用户判断用户否有权限,ssh登录中需要,函数定义也在pam_modules.h里,函数签名为

int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv);

使用 Go 实现

//export pam_sm_acct_mgmt func pam_sm_acct_mgmt(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int { return C.PAM_SUCCESS }

pam_prompt是认证过程中用于交互的函数,改函数直接调用,函数定义在 pam_ext.h 中,函数签名为

extern int PAM_FORMAT((printf, 4, 5)) PAM_NONNULL((1,4)) pam_prompt (pam_handle_t *pamh, int style, char **response, const char *fmt, ...);

注意 该函数中带有 ... 表示可以接受可变参数,而 CGO 中则不支持调用可变参数函数。因此需要使用 C 将该函数简单包装成一个固定参数的函数

int pam_prompt_wrapper (pam_handle_t *pamh, int style, char **response,const char *str) { return pam_prompt(pamh, style, response, "%s", str) }

并且该函数要额外使用 C 文件存储,并在 Go 中引入

代码文件

pam_prompt_wrapper.h

#include <security/pam_appl.h> int pam_prompt_wrapper(pam_handle_t *pamh, int style, char **response, const char *str);

pam_prompt_wrapper.c

#include <security/pam_appl.h> #include <security/pam_ext.h> #include <security/pam_modules.h> #include <stdarg.h> int pam_prompt_wrapper(pam_handle_t *pamh, int style, char **response, const char *str) { return pam_prompt(pamh, style, response, "%s", fmt); }

main.go

package main /* #cgo LDFLAGS: -lpam #include <stdio.h> #include <stdlib.h> #include <security/pam_appl.h> #include <security/pam_modules.h> #include <security/pam_ext.h> #include "pam_prompt_wrapper.h" typedef const char cchar_t; */ import "C" import ( "fmt" "os" "unsafe" ) //export pam_sm_authenticate func pam_sm_authenticate(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int { // 实现认证功能 // 例如: // 认证时会显示 input > // 输入 123 后认证成功,否则失败 a, _ := qa(pamh, false, "input >") if a == "123" { return C.PAM_SUCCESS } return C.PAM_AUTH_ERR } //export pam_sm_acct_mgmt func pam_sm_acct_mgmt(pamh *C.pam_handle_t, flags C.int, argc C.int, argv **C.cchar_t) C.int { // 这里直接返回成功,也可以根据自己需求添加更多功能 return C.PAM_SUCCESS } // 简单封装了交互函数 func qa(pamh *C.pam_handle_t, echo bool, query string) (string, C.int) { var f C.int f = C.PAM_PROMPT_ECHO_OFF if echo { f = C.PAM_PROMPT_ECHO_ON } input := C.CString("") defer C.free(unsafe.Pointer(input)) prompt := C.CString(query) defer C.free(unsafe.Pointer(prompt)) code := C.pam_prompt_wrapper(pamh, f, &input, prompt) return C.GoString(input), code } func main() { }

编译

需要将 Go 编译成 C 的共享库

go build -buildmode=c-shared -o pam_example.so

0x3 使用

需要将模块放置到指定目录

sudo cp pam_example.so /lib/x86_64-linux-gnu/security

然后启动,这里展示在 ssh 登录验证中使用我们的模块

首先编辑 /etc/ssh/sshd_config ,修改 ChallengeResponseAuthenticationKbdInteractiveAuthentication 选项的值将其设为 yes

重启 sshd 服务 sudo service sshd restart

编辑 /etc/pam.d/sshd ,在文件添加 auth required pam_example.so

Last modification:October 20, 2023
如果觉得我的文章对你有用,请随意赞赏