具体的用法、约定有很多,不在这里解释了,可以 man 一下相应的函数或者看看 pam
的文档。
总体设计
认证过程与标准的 UNIX 认证差不多,只不过密码是存储在卡上的随机数,不是通过键
盘输入的就是了。
首先是认证标记:
typedef struct {
int magic; /* 这里是'CARD',用来确认是不是一个有效的标记 */
int magic1; /* 这里也是'CARD',但是如果有 PIN 的话,这个标记会一起被
加密 */
int id; /* 一个随机数,只是个记号 */
char token[56]; /* 56个字节的随机数,充当密码使用 */
} token;
如果设定了 PIN的话,magic1、id和token[56]这三个域都要使用AES 加密存储,密钥
就是 PIN的 MD5值。
magic1、id和token[56]这几个加起来正好是64 个字节,也就是 AES blocksize的4
倍,这样就可以不要 padding 了;同时也是 SHA512 的输出的大小(下文会说到)。magic1
用来判断解密是否正确,PIN 不正确的话这里就不会是'CARD'。即使使用错误的PIN解密出
来的 magic1是'CARD',也不会通过接下来的测试。
存储在电脑上的"shadow"文件保存在/etc/cards,对每一个用户只储存这些项:用户名,
对应的认证标记种的 id,认证标记的 hash。hash 的算法作者采用的是重复 20000 次的
SHA512,hash的内容就是上一段说的那64个字节,这样可以省去第一次的特殊处理。
因为 SIM 卡中每一条电话号码记录是28 字节,每一条短信记录是 176 字节,所以认证
标记只能放到短信文件里(下文会说到)。
代码编写,PAM部分
#include <stdarg.h>
#include <sys/types.h>
#include <locale.h>
#include <libintl.h>
#define _ gettext
#define PAM_SM_AUTH /* 要求有 pam_sm_authenticate的定义 */
#include <security/_pam_macros.h>
#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include "common.h" /* 各种辅助函数, 会有相应的解释 */
#include "card.h" /* 智能卡的读写函数 */
#include "settings.h"
#define msg_error(args...) \
if(!(flags & PAM_SILENT)) { \
pam_prompt(pamh, PAM_ERROR_MSG, NULL, args); \
}
#define msg_info(args...) \
if(!(flags & PAM_SILENT)) { \
pam_prompt(pamh, PAM_TEXT_INFO, NULL, args); \
}
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
const char **argv)
/*
这个就是认证的函数了,他的返回值决定了认证是成功还是失败
pamh: 应用程序由pam_start函数获得的句柄
flags: 标志位 具体请看PAM 的文档
argc/argv: 在配置文件中指定的参数
*/
{
int retval = PAM_AUTHINFO_UNAVAIL; /* 返回代码 */
token tok, *ptok; /* 认证标记 */
userinfo *uih = NULL, *u; /* 机器上存储的"shadow"文件 */
const char *desired_user = NULL, *rhost = NULL;
char hash[64];
setlocale(LC_ALL, ""); /* 这3 句是 L10N 相关的 */
bindtextdomain("pam_iccard", ".");
textdomain("pam_iccard");
parse_file("/etc/pam_iccard.conf", 0);
parse_args(argc, (char**)argv, 1);
if(on("silent")) {
flags |= PAM_SILENT;
}
if(PAM_SUCCESS == pam_get_item(pamh, PAM_RHOST, (const void **)&rhost)) {
if(rhost && strlen(rhost)) {
/* 不支持远程登录:
远程登录的时候(比如 ssh),认证的结果取决于那台机器上是否有智能
卡,这很显然不合理 */
msg_error(_("pam_iccard doesn't support remote login."));
retval = PAM_AUTH_ERR;
goto last; |