FreeRadius : rlm_files

今天来分析一下FreeRadius的rlm_files模块。

rlm_files

The rlm_files module uses the ‘users’ file for accessing authorization information for users.

以上内容摘自man文档。

翻译过来:rlm_files模块使用’users’文件来获取用户的authorization信息。(疑问:总感觉这里应该是authentication)

也就是说,该模块的authorization阶段会使用users文件进行授权。users的位置是:/usr/local/etc/raddb/users, 它是一个软链接,链接到/usr/local/etc/raddb/mods-config/files/authorize。

users文件

我们来学习下users文件中的注释:

This file contains authentication security and configuration information for each user.

翻译:该文件包含每个user的authentication security和配置信息。(上面是authorization,这里又变成了authentication。>_<)

The first field is the user’s name and can be up to 253 characters in length. This is followed (on the same line) with the list of authentication requirements for that user. This can include password, comm server name, comm server port number, protocol type (perhaps set by the “hints” file), and huntgroup name (set by the “huntgroups” file).

文件中的第一个field是username,最长253个字符;username后面包含一些用户的authentication信息。例如:

bob Cleartext-Password := "hello"
    Reply-Message := "Hello, %{User-Name}"

其中bob就是用户名,接着是一些value-pair,如Cleartext-Password(明文密码)等

代码分析

typedef struct rlm_files_t {
    char const *compat_mode;
    char const *key;
    char const *filename; // users文件的路径
    rbtree_t *common;     // 将users中的内容保存到红黑树中
    ...
} rlm_files_t;

rlm_files_t结构体是该模块相关的上下文。它是怎么被使用的呢?

/*
 *  (Re-)read the "users" file into memory.
 */
static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
{
    rlm_files_t *inst = instance;

#undef READFILE
#define READFILE(_x, _y) do { if (getusersfile(inst, inst->_x, &inst->_y, inst->compat_mode) != 0) { ERROR("Failed reading %s", inst->_x); return -1;} } while (0)

    READFILE(filename, common);
    ...
    return 0;
}

其中,READFILE(filename, common) 宏展开为(去掉了do while和if):

getusersfile(inst, inst->filename, &inst->common, inst->compat_mode) ;

所以该函数的工作就是读取users文件的内容,保存到红黑树中。实际上,首先将文件内容保存到一个链表中,然后再将链表转换成红黑树,加快索引速度。PS:如果有兴趣,可以看看pairlist_read函数,可以学到字符串处理的一些用法。

对于一个模块,Radius Server会先调用它的mod_instantiate函数,该函数会进行一些初始化工作,对于rlm_files则是读取users文件等,并将这个过程中的一些数据称为模块的上下文context,该context的指针会被Radius Server保存起来。当Radius Server调用模块的其他函数时,会将context的指针传递过来。

/*
 *  Find the named user in the database.  Create the
 *  set of attribute-value pairs to check and reply with
 *  for this user from the database. The main code only
 *  needs to check the password, the rest is done here.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
    rlm_files_t *inst = instance;

    return file_common(inst, request, "users",
               inst->users ? inst->users : inst->common,
               request->packet, request->reply);
}

其中参数instance就是context的指针,需要使用时强制类型转换一下就可以了。

那么Cleartext-Password在哪里被使用的呢?
mschap模块的mod_authenticate会查看有没有Cleartext-Password:

    password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);

可以看到,是从request->config中查找的,密码是什么时候放到config中的呢?答案是rlm_files模块的file_common函数:

    if (paircompare(request, request_packet->vps, check_tmp, &reply_packet->vps) == 0) {
            RDEBUG2("%s: Matched entry %s at line %d", filename, match, pl->lineno);
            found = true;

            /* ctx may be reply or proxy */
            reply_tmp = fr_pair_list_copy(reply_packet, pl->reply);
            radius_pairmove(request, &reply_packet->vps, reply_tmp, true);
            fr_pair_list_move(request, &request->config, &check_tmp); // 将用户信息保存到request->config中,供其他authentication模块使用,如mschap
            fr_pair_list_free(&check_tmp);
    }

使用自定义的验证方法

FreeRadius的rlm_files模块会根据用户名username查找用户的密码,在这里可以自定义自己的验证方法,从而实现登陆。原始代码查找密码的地方在rlm_files.c的file_common函数中:

    my_pl.name = name; // 用户名
    user_pl = rbtree_finddata(tree, &my_pl);

rlm_files模块的初始化函数会将users文件中的用户信息读到内存中,使用红黑树保存,通过name可以找到对应的用户相关信息,包括用户的Clear-Password。我们完全可以替换掉rbtree_finddata函数,从而自定义一个登陆方式,只要构造出一个合理的user_pl供后续使用即可。

这里给出一个例子,我们自定义的认证规则是:只要密码是用户名的逆序就允许用户登陆,如username是qwer,那么password是rewq时才允许用户成功登陆。

确定了规则后,就可以修改rlm_files.c文件了,添加如下的函数:

/**
 * get password by name
 *
 * @param name entered by user.
 * @param pw is the password entered by user.
 * @param pwlen is the length of pw.
 * @return 0 for success or -1 for failed
 * @author hongjin.cao
 */
int jinger_get_pw_by_name(const char* name, char* pw, size_t *pwlen)
{
    int l = strlen(name);
    if(l >= *pwlen) return -1;
    for(int i = l - 1; i >= 0; i--)
        pw[l-i-1] = name[i];

    *pwlen = l;
    pw[l] = '\0';
    return 0;
}

/**
 * find user by name
 *
 * @note Inspired by function pairlist_read in src/main/files.c
 * @param request for talloc.
 * @param name entered by user.
 * @return a PAIR_LIST pointer storing user's information.
 * @author hongjin.cao
 */
PAIR_LIST * jinger_get_user_by_name(REQUEST *request, const char *name)
{
    PAIR_LIST *t;

    /* get password by name */
    char pw[256];
    size_t len = sizeof(pw);
    if(jinger_get_pw_by_name(name, pw, &len) != 0) return NULL;

    /* create a PAIR_LIST */
    VALUE_PAIR *check_tmp = NULL;
    char check_buff[256];
    sprintf(check_buff, "Cleartext-Password := %s", pw);
    fr_pair_list_afrom_str(request, check_buff, &check_tmp); // build a VALUE_PAIR named check_tmp

    VALUE_PAIR *reply_tmp = NULL;
    char reply_buff[256];
    sprintf(reply_buff, "Reply-Message := \"Welcome, %s\"", name);
    fr_pair_list_afrom_str(request,  reply_buff, &reply_tmp);

    MEM(t = talloc_zero(request, PAIR_LIST));

    if (check_tmp) fr_pair_steal(t, check_tmp);
    if (reply_tmp) fr_pair_steal(t, reply_tmp);

    t->check = check_tmp;
    t->reply = reply_tmp;
    t->lineno = -1; // fixed to -1
    t->name = talloc_typed_strdup(t, name);
    t->next = NULL;

    return t;
}

并将file_common函数的

    user_pl = rbtree_finddata(tree, &my_pl);

改成

    user_pl = jinger_get_user_by_name(request, name);

这样就不再使用users文件的内容验证,而是使用自定义的规则进行验证了。

PS:其实真正的验证不在rlm_files中,而在mschap模块中。rlm_files只负责通过用户名找到用户的密码,保存到request.config中,供mschap模块使用来验证用户输入的密码是否正确。

认证流程总结

  • Radius Server启动,读取users文件到内存中,以红黑树保存。
  • 用户输入username和password发起登陆请求。
  • 服务器的rlm_files模块根据username从红黑树中找到对应的用户信息。然后保存到request.config中
  • mschap模块会从request.config中取出用户信息,这包括一个Cleartext-Password,mschap会用这个明文密码生成需要的NT-Password,然后验证用户的密码是否正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值