Restructure pam conversation function

Altered return values form the function. With the earlier
implementation, the function returned PAM_AUTH_ERR on failure scenarios
which is incorrect. Replaced PAM_AUTH_ERR with PAM_CONV_ERR and
PAM_BUF_ERR at respetive places.

Added a check for number of messages received by the conversation
function capped at PAM_MAX_NUM_MSG.

Added a check for password size, which is capped at PAM_MAX_RESP_SIZE
as the bytes in the password greater than this limit would be discarded
by PAM.

Though pam_response structure and response, which are dynamically
allocated by the pam conversation function are the responsibility of the
caller to free them, with the current implemention, there is a possibility of
memory leak when numMsg would be zero or if PAM_PROMPT_ECHO_OFF
message never arrived.
This commit fixes the possible memory leak by allocating only on
receiving PAM_PROMPT_ECHO_OFF message.

Tested:
 - Basic Authencation is functional.
 - POST on /redfish/v1/SessionService/Sessions was succesfull with
   the right credentials
 - POST on /redfish/v1/AccountService was successfull when the password
   was within the limit, and returned a failure when password length
   exceeded the limit.

Signed-off-by: P Dheeraj Srujan Kumar <p.dheeraj.srujan.kumar@intel.com>
Change-Id: Idfa41d94d5a01b62aec119f88cbdaab1523ad936
diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp
index 12f19c0..a24270f 100644
--- a/include/pam_authenticate.hpp
+++ b/include/pam_authenticate.hpp
@@ -13,28 +13,14 @@
 {
     if (appdataPtr == nullptr)
     {
-        return PAM_AUTH_ERR;
+        return PAM_CONV_ERR;
     }
-    char* appPass = reinterpret_cast<char*>(appdataPtr);
-    size_t appPassSize = std::strlen(appPass);
-    char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
-    if (pass == nullptr)
+
+    if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
     {
-        return PAM_AUTH_ERR;
+        return PAM_CONV_ERR;
     }
 
-    std::strncpy(pass, appPass, appPassSize + 1);
-
-    void* ptr =
-        calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
-    if (ptr == nullptr)
-    {
-        free(pass);
-        return PAM_AUTH_ERR;
-    }
-
-    *resp = reinterpret_cast<pam_response*>(ptr);
-
     for (int i = 0; i < numMsg; ++i)
     {
         /* Ignore all PAM messages except prompting for hidden input */
@@ -44,10 +30,39 @@
         }
 
         /* Assume PAM is only prompting for the password as hidden input */
+        /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
+
+        char* appPass = reinterpret_cast<char*>(appdataPtr);
+        size_t appPassSize = std::strlen(appPass);
+
+        if ((appPassSize + 1) > PAM_MAX_RESP_SIZE)
+        {
+            return PAM_CONV_ERR;
+        }
+
+        char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
+        if (pass == nullptr)
+        {
+            return PAM_BUF_ERR;
+        }
+
+        std::strncpy(pass, appPass, appPassSize + 1);
+
+        void* ptr =
+            calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response));
+        if (ptr == nullptr)
+        {
+            free(pass);
+            return PAM_BUF_ERR;
+        }
+
+        *resp = reinterpret_cast<pam_response*>(ptr);
         resp[i]->resp = pass;
+
+        return PAM_SUCCESS;
     }
 
-    return PAM_SUCCESS;
+    return PAM_CONV_ERR;
 }
 
 /**