5.1. 模块创建的安全性问题

5.1.1. 足够的资源

应注意确保模块的正确执行不会因缺乏系统资源而受到影响。如果模块无法打开足够的文件来执行其任务,则该模块应正常运行,或请求其他资源。具体来说,应该考虑由 setrlimit(2)系列命令操纵的数量。

5.1.2. 谁的谁?

通常,模块可能希望构建请求服务的用户的身份。这可能与pam_get_user()返回的用户名不同。实际上,这仅是将以其身份提供服务的用户的名称。这不一定是请求服务的用户。

换句话说,用户 X 运行一个 setuid-Y 程序,它授予用户 Z 的权限。此类服务请求的一个特定示例是 su 程序:user joe 执行 su 成为用户 jane 。在这种情况下,X = joe ,Y = root 和 Z = jane 。显然,重要的是模块不要混淆这些不同的用户并授予不适当的特权级别。

以下是在处理用户身份时要遵守的约定。

  • X,调用服务请求的用户的身份。这是用户标识符;由 getuid(2)函数返回。

  • Y,用于授予请求的服务的应用程序的特权身份。这是有效用户标识符;由 geteuid(2)函数返回。

  • Z,将以其身份授予服务的用户。这是pam_get_user()返回的用户名,也存储在* Linux-PAM 项目 PAM_USER *中。

    • Linux-PAM 为模块可能需要使用的其他用户身份提供了一个位置。这是 PAM_RUSER *项目。通常,网络敏感模块/应用可能希望设置/读取该项目以构建从远程位置请求服务的用户的身份。

注意,如果模块希望修改正在运行的进程的* uid euid 的标识,则在将控制权返回给 Linux-PAM *库之前,应注意恢复原始值。

5.1.3. 使用对话功能

在调用对话功能之前,模块应重置指针的内容,该内容将返回应用程序响应。这是一个好主意,因为应用程序可能无法填充指针,并且模块应该可以注意到!

该模块应为对话失败做好准备。通用错误将是* PAM_CONV_ERR ,但是 PAM_SUCCESS *以外的任何错误都应视为指示失败。

5.1.4. 认证令牌

为了确保身份验证令牌不会留在项目周围,* PAM_AUTHTOK PAM_OLDAUTHTOK 对于应用程序不可用:它们在<security/pam_modules.h>中定义。表面上看,这是出于安全原因,但是经过恶意编程的应用程序始终可以访问该进程的所有内存,因此仅是表面执行。通常,一旦不再需要身份验证令牌,该模块便应覆盖它们。特别是在free()之前。 (重置)这些身份验证令牌项中的任何一个时,都需要 Linux-PAM *库。

不要过多地关注这个问题;如果模块将身份验证令牌存储为(自动)函数变量或使用pam_[gs]et_data(),则相关的内存在释放之前应被显式覆盖。对于后一种存储机制,相关的cleanup()函数应在free()之前明确覆盖*data:例如,

/*
 * An example cleanup() function for releasing memory that was used to
 * store a password.
 */

int cleanup(pam_handle_t *pamh, void *data, int error_status)
{
    char *xx;

    if ((xx = data)) {
        while (*xx)
            *xx++ = '\0';
        free(data);
    }
    return PAM_SUCCESS;
}