C++ 遗留应用程序的PAM身份验证

C++ 遗留应用程序的PAM身份验证,c++,c,linux,authentication,pam,C++,C,Linux,Authentication,Pam,我有一个传统的应用程序,通过网络异步接收用户名/密码请求。既然我已经将用户名和密码存储为变量,那么在Linux(Debian 6)上使用PAM进行身份验证的最佳方法是什么 我已经尝试编写自己的对话函数,但我不确定将密码输入其中的最佳方式。我考虑过将其存储在appdata中,并从pam_conv结构中引用它,但几乎没有关于如何做到这一点的文档 有没有一种更简单的方法来验证用户,而不必过度使用会话功能?我也无法成功地使用pam_set_数据,我甚至不确定这是否合适 以下是我正在做的: user =

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

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

有没有一种更简单的方法来验证用户,而不必过度使用会话功能?我也无法成功地使用pam_set_数据,我甚至不确定这是否合适

以下是我正在做的:

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*数据)
{
结构pam_响应*arep;
if(num\u msg PAM\u MAX\u num\u msg)
返回(PAM_CONV_ERR);
if((arep=(pam_response*)calloc(num_msg,sizeof*arep))==NULL)
返回(PAM_BUF_ERR);
aresp[0]。响应代码=0;
aresp[0]。resp=strdup(“我的密码”);
*resp=aresp;
返回(PAM_SUCCESS);
}

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

这就是我最后要做的。请参见标有三个星号的注释

#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;
}
#包括
#包括
#包括
#包括
#包括
//要构建此功能,请执行以下操作:
//g++test.cpp-lpam-o测试
//如果缺少pam头文件,请尝试:
//sudo apt安装libpam0g dev
结构pam_响应*回复;
//用于获取用户输入的函数
int function_对话(int num_msg,常量结构pam_消息**msg,结构pam_响应**resp,void*appdata_ptr)
{
*resp=回复;
返回PAM_成功;
}
int main(int argc,字符**argv)
{
如果(argc!=2){
fprintf(stderr,“用法:检查用户\n”);
出口(1);
}
const char*用户名;
用户名=argv[1];
const struct pam_conv local_conversation={function_conversation,NULL};
pam\u handle\u t*local\u auth\u handle=NULL;//这由pam\u start设置
内部检索;
//基于服务设置本地身份验证句柄
retval=pam_start(“公共身份验证”、用户名、本地对话和本地身份验证句柄);
if(retval!=PAM_SUCCESS)
{

std::cout传递PAM的标准信息(如密码)的方式是使用PAM句柄中设置的变量和PAM_set_项(请参阅PAM_set_项的手册页)

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

pam_handle_t* handle = NULL;
pam_start("common-auth", username, NULL, &handle);
pam_set_item( handle, PAM_AUTHTOK, password);
这将使密码在堆栈上可供任何想要使用它的模块使用,但通常您必须通过设置标准的use\u first\u pass来告诉模块使用密码,或者在服务的pam\u配置中尝试使用\u first\u pass选项(在本例中为/etc/pam.d/common auth)

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

执行此操作后,从公共身份验证服务调用的对pam\u authenticate()的任何调用都应该选择密码并使用它


关于use_first_pass和try_first_pass之间的区别,有一点需要注意:它们都告诉模块(在本例中是pam_unix)尝试pam_堆栈上的密码,但在没有密码/AUTHTOK可用时,它们的行为不同。在缺少密码/AUTHTOK的情况下,use_first_pass失败,而try_first_pass允许模块提示输入密码。

Fantius的解决方案对我有效,即使作为root用户

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

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

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

结构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);
    }
static int convCallback(int num_msg,const struct pam_message**msg,
结构pam_响应**resp,void*appdata_ptr)
{
结构pam_响应*arep;
if(num\u msg PAM\u MAX\u num\u msg)
返回(PAM_CONV_ERR);
if((arep=(pam_response*)calloc(num_msg,sizeof*arep))==NULL)
返回(PAM_BUF_ERR);
aresp[0]。响应代码=0;
aresp[0]。resp=strdup((char*)appdata_ptr);
*resp=aresp;
返回(PAM_SUCCESS);
}
int main()
{
....
pam_handle_t*pamH=0;
char*password=strdup(“foopassword”);
结构pam_conv conversation={convCallback,password};
int retvalPam=pam_start(“检查用户”、“foousername”、“对话”和pamH);
//调用pam_进行身份验证(pamH,0)
//调用pam_end(pamH,0);
...
...
免费(密码);
}

谢谢!在这里,我试图找到沟通到对话功能中的方法,而不是绕过并处理对话功能之外的响应。这对“root”不起作用(只有“root”,所有其他用户都已授权)。
struct pam_conv {
    int (*conv)(int num_msg, const struct pam_message **msg,
                struct pam_response **resp, void *appdata_ptr);
    void *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);
    }