【问题标题】:PAM Authentication for a Legacy Application旧应用程序的 PAM 身份验证
【发布时间】:2020-10-20 18:38:46
【问题描述】:

我有一个旧版应用程序,它通过网络异步接收用户名/密码请求。由于我已经将用户名和密码存储为变量,在 Linux (Debian 6) 上使用 PAM 进行身份验证的最佳方法是什么?

我已经尝试编写自己的对话函数,但我不确定将密码输入其中的最佳方法。我考虑过将它存储在 appdata 中并从 pam_conv 结构中引用它,但几乎没有关于如何做到这一点的文档。

有没有一种更简单的方法来验证用户而无需过度使用对话功能?我也无法成功使用 pam_set_data,我不确定这是否合适。

这就是我正在做的事情:

user = guiMessage->username;
pass = guiMessage->password;

pam_handle_t* pamh = NULL;
int           pam_ret;
struct pam_conv conv = {
  my_conv,
  NULL
};

pam_start("nxs_login", user, &conv, &pamh);
pam_ret = pam_authenticate(pamh, 0);

if (pam_ret == PAM_SUCCESS)
  permissions = 0xff;

pam_end(pamh, pam_ret);

对话功能的初始尝试导致(密码被硬编码以进行测试):

int 
my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *data)
{
  struct pam_response *aresp;

  if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
    return (PAM_CONV_ERR);
  if ((aresp = (pam_response*)calloc(num_msg, sizeof *aresp)) == NULL)
    return (PAM_BUF_ERR);
  aresp[0].resp_retcode = 0;
  aresp[0].resp = strdup("mypassword");

  *resp = aresp;
  return (PAM_SUCCESS);
}

任何帮助将不胜感激。谢谢!

【问题讨论】:

    标签: c++ c linux authentication pam


    【解决方案1】:

    这就是我最终要做的。请参阅标有三个星号的评论。

    #include <stdlib.h>
    #include <iostream>
    #include <fstream>
    #include <security/pam_appl.h>
    #include <unistd.h>
    
    // To build this:
    // g++ test.cpp -lpam -o test
    
    // if pam header files missing try:
    // sudo apt install libpam0g-dev
    
    struct pam_response *reply;
    
    //function used to get user input
    int function_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
    {
      *resp = reply;
      return PAM_SUCCESS;
    }
    
    int main(int argc, char** argv)
    {
      if(argc != 2) {
          fprintf(stderr, "Usage: check_user <username>\n");
          exit(1);
      }
      const char *username;
      username = argv[1];
    
      const struct pam_conv local_conversation = { function_conversation, NULL };
      pam_handle_t *local_auth_handle = NULL; // this gets set by pam_start
    
      int retval;
    
      // local_auth_handle gets set based on the service
      retval = pam_start("common-auth", username, &local_conversation, &local_auth_handle);
    
      if (retval != PAM_SUCCESS)
      {
        std::cout << "pam_start returned " << retval << std::endl;
        exit(retval);
      }
    
      reply = (struct pam_response *)malloc(sizeof(struct pam_response));
    
      // *** Get the password by any method, or maybe it was passed into this function.
      reply[0].resp = getpass("Password: ");
      reply[0].resp_retcode = 0;
    
      retval = pam_authenticate(local_auth_handle, 0);
    
      if (retval != PAM_SUCCESS)
      {
        if (retval == PAM_AUTH_ERR)
        {
          std::cout << "Authentication failure." << std::endl;
        }
        else
        {
          std::cout << "pam_authenticate returned " << retval << std::endl;
        }
        exit(retval);
      }
    
      std::cout << "Authenticated." << std::endl;
    
      retval = pam_end(local_auth_handle, retval);
    
      if (retval != PAM_SUCCESS)
      {
        std::cout << "pam_end returned " << retval << std::endl;
        exit(retval);
      }
    
      return retval;
    }
    

    【讨论】:

    • 谢谢!在这里,我试图找到与对话功能进行通信进入的方法,而不是仅仅绕过并处理它之外的响应。
    • 这不适用于“root”(只是“root”,所有其他用户都可以进行身份​​验证)。这是一个错误吗?
    • 我不知道。我没有尝试root。如果您需要定期使用 root 密码,您可能做错了。
    • 您可以甚至应该使用appdata。它是您传递给 PAM 的任意指针,然后 PAM 在对话函数的回调中将其交还给您。这样您就可以传递任意数据。它可以是指向getpass 函数的指针,也可以是指向您传递给getpass 的字符串或指向密码本身的指针。任何。要点是允许您避免必须是全局变量才能在您的函数和会话函数之间共享。
    • 如果任何 PAM 模块尝试在一个对话函数调用中处理多条消息,此解决方案将导致堆读取越界!这种情况很少见,但可能会发生。在这种情况下,*resp 应该是 num_msg pam_response 结构的数组,而不是指向一个结构的指针。
    【解决方案2】:

    为 PAM 传递标准信息(例如密码)的方式是使用 pam_set_item 在 pam 句柄中设置的变量(请参阅 pam_set_item 的手册页)。

    您可以将应用程序稍后需要使用的任何内容设置到 pam_stack 中。如果您想将密码放入 pam_stack 中,您应该能够在调用 pam_start() 后立即执行此操作,方法是将 PAM_AUTHTOK 变量设置到堆栈中,类似于下面的伪代码:

    pam_handle_t* handle = NULL;
    pam_start("common-auth", username, NULL, &handle);
    pam_set_item( handle, PAM_AUTHTOK, password);
    

    这将使密码在堆栈上可供任何关心使用它的模块使用,但您通常必须通过在服务的 pam_configuration 中设置标准 use_first_pass 或 try_first_pass 选项来告诉模块使用它(在此案例 /etc/pam.d/common-auth)。

    标准的 pam_unix 模块确实支持 try_first_pass,因此将它添加到系统上的 pam 配置中不会有什么坏处(在 pam_unix 行的末尾)。

    执行此操作后,从 common-auth 服务调用的任何对 pam_authenticate() 的调用都应该只选择密码并使用它。

    关于 use_first_pass 和 try_first_pass 之间的区别的一个小说明:它们都告诉模块(在本例中为 pam_unix)在 pam_stack 上尝试密码,但是当它们没有密码/AUTHTOK 可用时,它们的行为不同。在缺少的情况下 use_first_pass 失败,并且 try_first_pass 允许模块提示输入密码。

    【讨论】:

    【解决方案3】:

    Fantius 的解决方案对我有用,即使是 root。

    我最初选择了 John 的解决方案,因为它更简洁,并且在没有对话功能的情况下使用了 PAM 变量(实际上,这里不需要它),但它没有,也不会工作。正如 Adam Badura 在两篇文章中提到的那样,PAM 有一些内部检查来防止直接设置 PAM_AUTHTOK。

    John 的解决方案将导致类似于提到的 here 的行为,其中允许任何密码值登录(即使您声明但未定义 pam_conv 变量)。

    我还建议用户注意 malloc 的位置,因为它在您的应用程序中可能会有所不同(请记住,上面的代码更像是一个测试/模板,而不是其他任何东西)。

    【讨论】:

    • 感谢您提醒我为什么我在这类网站上很随意;)我很抱歉!
    【解决方案4】:
    struct pam_conv {
        int (*conv)(int num_msg, const struct pam_message **msg,
                    struct pam_response **resp, void *appdata_ptr);
        void *appdata_ptr;
    };
    

    struct pam_conv 的第二个字段(appdata_ptr) 被传递给对话函数, 因此我们可以使用它作为我们的密码指针。

         static int convCallback(int num_msg, const struct pam_message** msg,
                                 struct pam_response** resp, void* appdata_ptr)
         {
                struct pam_response* aresp;
            
                if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
                    return (PAM_CONV_ERR);
                if ((aresp = (pam_response*)calloc(num_msg, sizeof * aresp)) == NULL)
                    return (PAM_BUF_ERR);
                aresp[0].resp_retcode = 0;
                aresp[0].resp = strdup((char*)appdata_ptr);
                                                        
                *resp = aresp;
                
                return (PAM_SUCCESS);
        }
    
        int main()
        {
            ....
            pam_handle_t* pamH = 0;
            char *password = strdup("foopassword");
            struct pam_conv conversation = {convCallback, password};
            int retvalPam = pam_start("check_user", "foousername", &conversation, &pamH);
            
            //Call pam_authenticate(pamH, 0)
            //Call pam_end(pamH, 0);
            ...
            ...
            free(password);
        }
    

    【讨论】:

      猜你喜欢
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-18
      相关资源
      最近更新 更多