+/* states for pamconv() */
+enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN;
+/* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */
+static int password_change_required = 0;
+/* remember whether the last pam_authenticate() succeeded or not */
+static int was_authenticated = 0;
+
+/* accessor which allows us to switch conversation structs according to
+ * the authentication method being used */
+void pam_set_conv(struct pam_conv *conv)
+{
+ pam_set_item(pamh, PAM_CONV, conv);
+}
+
+/* start an authentication run */
+int do_pam_authenticate(int flags)
+{
+ int retval = pam_authenticate(pamh, flags);
+ was_authenticated = (retval == PAM_SUCCESS);
+ return retval;
+}
+
+/*
+ * PAM conversation function.
+ * There are two states this can run in.
+ *
+ * INITIAL_LOGIN mode simply feeds the password from the client into
+ * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output
+ * messages with pam_msg_cat(). This is used during initial
+ * authentication to bypass the normal PAM password prompt.
+ *
+ * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1)
+ * and outputs messages to stderr. This mode is used if pam_chauthtok()
+ * is called to update expired passwords.
+ */