9

ネットワーク経由でユーザー名/パスワード要求を非同期に受信する従来のアプリがあります。ユーザー名とパスワードが変数として保存されているため、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);
}

どんな助けでも大歓迎です。ありがとうございました!

4

4 に答える 4

15

これが私がやったことです。3 つのアスタリスクでマークされたコメントを参照してください。

#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;
}
于 2011-05-11T20:16:33.630 に答える
2

PAM の標準情報 (パスワードなど) が渡される方法は、pam_set_item で pam ハンドルに設定された変数を使用することです (pam_set_item のマニュアル ページを参照してください)。

アプリケーションが後で使用する必要があるものは何でも pam_stack に設定できます。パスワードを pam_stack に入れたい場合は、以下の疑似コードのように PAM_AUTHTOK 変数をスタックに設定することにより、pam_start() を呼び出した直後にそれを行うことができるはずです。

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 によってモジュールがパスワードを要求できるようになります。

于 2012-08-28T17:17:09.717 に答える
1
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 の 2 番目のフィールド (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);
    }
于 2020-10-20T04:13:13.533 に答える