If the user doesn't supply any arguments to the plugin in the OpenVPN
configuration file, then it defaults to answering any PAM conversation
'questions' where the pam module sets the message style to
PAM_PROMPT_ECHO_OFF with the password, and where the style is set to
PAM_PROMPT_ECHO_ON with the username.

If the administrator supplies one or more arguments to the auth-pam
plugin, then this simple but robust default behaviour is abandoned,
and the user must supply a match for the message text content (NOT the
message style) for ALL messages.  Non-matching messages are answered
with a NULL.

This patch restores the default behaviour for PAM_PROMPT_ECHO_OFF
style questions, and returns the password if no prior match has been
found from the explicit list which the administrator specified.  The
aim is to make the default behaviour less surprising for the user, and
also to make deployments less likely to break from changes in what are
indended to be human-readable messages (and which may change due to
e.g. localisation coverage improvements).

Signed-off-by: Tim Small <t...@seoss.co.uk>
---
 src/plugins/auth-pam/auth-pam.c | 107 ++++++++++++++++++++++++++--------------
 1 file changed, 71 insertions(+), 36 deletions(-)

diff --git a/src/plugins/auth-pam/auth-pam.c b/src/plugins/auth-pam/auth-pam.c
index fd46b99..e0dc029 100644
--- a/src/plugins/auth-pam/auth-pam.c
+++ b/src/plugins/auth-pam/auth-pam.c
@@ -538,6 +538,53 @@ openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle)
 }

 /*
+ * Key value pair list search with USERNAME/COMMONNAME/PASSWORD
+ * substitution.  Callee must arrange to free() the response.
+ */
+
+int
+get_value_with_subst (struct user_pass const *up, const char * const 
search_term, char **response)
+{
+  int ret = PAM_CONV_ERR;
+  int j;
+  const struct name_value_list *list = up->name_value_list;
+  /* loop through name/value pairs */
+  for (j = 0; j < list->len; ++j)
+    {
+      const char *item_key = list->data[j].name;
+      const char *item_value = list->data[j].value;
+
+      if (name_value_match (search_term, item_key))
+       {
+         /* found name/value match */
+         *response = NULL;
+
+         if (DEBUG (up->verb))
+           fprintf (stderr, "AUTH-PAM: BACKGROUND: name match found, 
query/match-string ['%s', '%s'] = '%s'\n",
+                    search_term,
+                    item_key,
+                    item_value);
+
+         if (strstr(item_value, "USERNAME"))
+           *response = searchandreplace(item_value, "USERNAME", up->username);
+         else if (strstr(item_value, "PASSWORD"))
+           *response = searchandreplace(item_value, "PASSWORD", up->password);
+         else if (strstr(item_value, "COMMONNAME"))
+           *response = searchandreplace(item_value, "COMMONNAME", 
up->common_name);
+         else
+           *response = strdup (item_value);
+
+         if (*response != NULL)
+           ret = PAM_SUCCESS;
+         break;
+       }
+    }
+  return ret;
+}
+
+
+
+/*
  * PAM conversation function
  */
 static int
@@ -574,47 +621,35 @@ my_conv (int n, const struct pam_message **msg_array,
       if (up->name_value_list && up->name_value_list->len > 0)
        {
          /* use name/value list match method */
-         const struct name_value_list *list = up->name_value_list;
-         int j;
-
-         /* loop through name/value pairs */
-         for (j = 0; j < list->len; ++j)
-           {
-             const char *match_name = list->data[j].name;
-             const char *match_value = list->data[j].value;
-
-             if (name_value_match (msg->msg, match_name))
-               {
-                 /* found name/value match */
-                 aresp[i].resp = NULL;
-
-                 if (DEBUG (up->verb))
-                   fprintf (stderr, "AUTH-PAM: BACKGROUND: name match found, 
query/match-string ['%s', '%s'] = '%s'\n",
-                            msg->msg,
-                            match_name,
-                            match_value);
-
-                 if (strstr(match_value, "USERNAME"))
-                   aresp[i].resp = searchandreplace(match_value, "USERNAME", 
up->username);
-                 else if (strstr(match_value, "PASSWORD"))
-                   aresp[i].resp = searchandreplace(match_value, "PASSWORD", 
up->password);
-                 else if (strstr(match_value, "COMMONNAME"))
-                   aresp[i].resp = searchandreplace(match_value, "COMMONNAME", 
up->common_name);
-                 else
-                   aresp[i].resp = strdup (match_value);
-
-                 if (aresp[i].resp == NULL)
-                   ret = PAM_CONV_ERR;
-                 break;
+         ret = get_value_with_subst (up, msg->msg, &(aresp[i].resp));
+         if (ret == PAM_CONV_ERR)
+            {
+             /*
+              * No match for requested message, but the pam module asked
+              * that the user-entered-data for this response shouldn't be
+              * displayed, so we will provide the password.  If the user
+              * doesn't want this behaviour they can add an explicit
+              * match for this pam message, and return something else.
+              */
+             if (msg->msg_style == PAM_PROMPT_ECHO_OFF)
+               {
+                  if (DEBUG (up->verb))
+                    fprintf (stderr, "AUTH-PAM: BACKGROUND: Got non-matching 
request for "
+                            "'%s' with style set to PAM_PROMPT_ECHO_OFF - "
+                            "sending password...\n", msg->msg);
+                  aresp[i].resp = strdup (up->password);
+                 if (aresp[i].resp != NULL)
+                   ret = PAM_SUCCESS;
+               } else {
+                  if (DEBUG (up->verb))
+                   fprintf (stderr, "AUTH-PAM: BACKGROUND: Failed to find a 
match for "
+                            "'%s' which was requested by pam module.\n", 
msg->msg);
                }
            }
-
-         if (j == list->len)
-           ret = PAM_CONV_ERR;
        }
       else
        {
-         /* use PAM_PROMPT_ECHO_x hints */
+         /* no name/value list provided, rely on PAM_PROMPT_ECHO_x hints */
          switch (msg->msg_style)
            {
            case PAM_PROMPT_ECHO_OFF:
-- 
2.1.4


Reply via email to