PAM Authentication for a Legacy Application

前端 未结 4 1555
余生分开走
余生分开走 2020-12-31 16:47

I have a legacy app that receives a username/password request asynchronously over the wire. Since I already have the username and password stored as variables, what would b

相关标签:
4条回答
  • 2020-12-31 17:04

    Fantius' solution worked for me, even as root.

    I originally opted for John's solution, as it was cleaner and made use of PAM variables without the conversation function (really, there isn't a need for it here), but it did not, and will not, work. As Adam Badura alluded to in both posts, PAM has some internal checks to prevent direct setting of PAM_AUTHTOK.

    John's solution will result in behaviour similar to what is mentioned here, where any password value will be allowed to login (even if you declare, but do not define, the pam_conv variable).

    I would also recommend users be aware of the placement of the malloc, as it will likely differ in your application (remember, the code above is more of a test/template, than anything else).

    0 讨论(0)
  • The way standard information (such as a password) is passed for PAM is by using variables set in the pam handle with pam_set_item (see the man page for pam_set_item).

    You can set anything your application will need to use later into the pam_stack. If you want to put the password into the pam_stack you should be able to do that immediately after calling pam_start() by setting the PAM_AUTHTOK variable into the stack similar to the pseudo code below:

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

    This will make the password available on the stack to any module that cares to use it, but you generally have to tell the module to use it by setting the standard use_first_pass, or try_first_pass options in the pam_configuration for the service (in this case /etc/pam.d/common-auth).

    The standard pam_unix module does support try_first_pass, so it wouldn't hurt to add that into your pam configuration on your system (at the end of the line for pam_unix).

    After you do this any call to pam_authenticate() that are invoked from the common-auth service should just pick the password up and go with it.

    One small note about the difference between use_first_pass and try_first_pass: They both tell the module (in this case pam_unix) to try the password on the pam_stack, but they differ in behavior when their is no password/AUTHTOK available. In the missing case use_first_pass fails, and try_first_pass allows the module to prompt for a password.

    0 讨论(0)
  • 2020-12-31 17:19

    This is what I ended up doing. See the comment marked with three asterisks.

    #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;
    }
    
    0 讨论(0)
  • 2020-12-31 17:30
    struct pam_conv {
        int (*conv)(int num_msg, const struct pam_message **msg,
                    struct pam_response **resp, void *appdata_ptr);
        void *appdata_ptr;
    };
    

    The second field(appdata_ptr) of the struct pam_conv is passed to the conversation function, therefore we can use it as our password pointer.

         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);
        }
    
    0 讨论(0)
提交回复
热议问题