Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37498812
en ru br
Репозитории ALT

Группа :: Графические оболочки/KDE
Пакет: sddm

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: alt-expired-password-handling.patch
Скачать


diff --git a/components/2.0/PasswordBox.qml b/components/2.0/PasswordBox.qml
index 8973eb2..76ddd6c 100644
--- a/components/2.0/PasswordBox.qml
+++ b/components/2.0/PasswordBox.qml
@@ -37,6 +37,8 @@ FocusScope {
     property alias textColor: txtMain.textColor
     property alias echoMode: txtMain.echoMode
     property alias text: txtMain.text
+    property alias validator: txtMain.validator
+    property alias maximumLength: txtMain.maximumLength
 
     property alias image: img.source
     property double imageFadeIn: 300
@@ -47,6 +49,8 @@ FocusScope {
     property alias tooltipFG: tooltipText.color
     property alias tooltipBG: tooltip.color
 
+    signal accepted
+
     TextConstants {
         id: textConstants
     }
@@ -57,8 +61,12 @@ FocusScope {
         font.pixelSize: 14
 
         echoMode: TextInput.Password
+        // default validator
+        validator: RegExpValidator { regExp: /[][!"б╖#$%&'()*+,./:;<=>?@\^_б╢`{|}~a-zA-Z0-9-]*/ }
 
         focus: true
+
+        onAccepted: container.accepted()
     }
 
     Image {
diff --git a/components/2.0/PasswordConnections.qml b/components/2.0/PasswordConnections.qml
new file mode 100644
index 0000000..d38d8b3
--- /dev/null
+++ b/components/2.0/PasswordConnections.qml
@@ -0,0 +1,120 @@
+/***************************************************************************
+* Copyright (c) 2017 Thomas Hoehn <thomas_hoehn@gmx.net>
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge,
+* publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*
+* Note: container for password renewal logic
+*
+***************************************************************************/
+
+import QtQuick 2.0
+import SddmComponents 2.0
+
+Item {
+
+    property var pwdItem
+    property var renewalDialog
+
+    // if provided supposed to have text property
+    property var errMsg
+    property var txtMsg
+
+    // gets focus when password renewal dialog closes
+    property var getsBackFocus
+
+    TextConstants { id: textConstants }
+
+    function clearPwd() {
+        if(typeof pwdItem.password !== "undefined")
+            pwdItem.password = ""
+        else if(typeof pwdItem.text !== "undefined")
+            pwdItem.text = ""
+    }
+
+    // handles password renewal events
+    Connections {
+
+        target: renewalDialog
+
+        onOk: {
+            sddm.pamResponse(renewalDialog.password)
+        }
+
+        onCancel: {
+            sddm.cancelPamConv()
+        }
+
+        onError: {
+            // unlikely: invalid prompt,
+            // i.e. new/repeat prompt for dialog not found in request
+            if(typeof errMsg !== "undefined")
+                errMsg.text = msg
+        }
+
+        onVisibleChanged: {
+            renewalDialogTimer.stop()
+
+            if(!renewalDialog.visible) {
+                renewalDialog.clear() // clear pam infos
+                clearPwd()
+
+                // last selected user or input field gets focus back
+                if(typeof getsBackFocus !== "undefined")
+                    getsBackFocus.forceActiveFocus()
+            }
+        }
+    }
+
+    Connections {
+
+        target: sddm
+
+        onLoginSucceeded: {
+            // ...will not be reached as greeter stops after successfull login...
+        }
+
+        onLoginFailed: {
+            if(typeof txtMsg !== "undefined")
+            {
+                txtMsg.text = textConstants.loginFailed
+                // filter out login failure details
+                if(err_msg != "Authentication failure" &&
+                   err_msg != "Password change aborted." &&
+                   err_msg != "Authentication token manipulation error")
+                   txtMsg.text += "\n" + err_msg
+            }
+            renewalDialog.visible = false
+            clearPwd()
+        }
+
+        // show messages from pam conversation (for expired passwords)
+        onPamConvMsg: {
+            // from signal pamConvMsg(pam_msg)
+            renewalDialog.append(pam_msg)
+        }
+
+        // new pam request arrived, e.g. for expired password,
+        onPamRequest: {
+            // open password renewalDialog dialog and block other GUI
+            renewalDialog.show(request.findNewPwdMessage(),
+                               request.findRepeatPwdMessage())
+        }
+    }
+}
diff --git a/components/2.0/PasswordRenewal.qml b/components/2.0/PasswordRenewal.qml
new file mode 100644
index 0000000..72c4890
--- /dev/null
+++ b/components/2.0/PasswordRenewal.qml
@@ -0,0 +1,276 @@
+/***************************************************************************
+* Copyright (c) 2017 Thomas Hoehn <Hoehn.Thomas@heidenhain.de>
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge,
+* publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+* OR OTHER DEALINGS IN THE SOFTWARE.
+*
+* Note: simple password input dialog for user with expired password (pam conversation)
+*
+***************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.2
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+
+FocusScope {
+    id: container
+    implicitHeight: dialogHeight
+    implicitWidth: infosWidth+2*margins
+
+    readonly property string password: pwd1Input.text
+
+    // prompt messages from pam
+    property alias prompt1: prompt1Txt.text
+    property alias prompt2: prompt2Txt.text
+
+    // dialog window
+    property int dialogHeight: 180
+    property int margins: 8
+
+    // infos text area with pam messages
+    property int infosWidth: 520
+    property int infosHeight: 10
+
+    property color promptColor: "black"
+    property color infosColor: "red"
+    property alias color: dialog.color // dialog background
+    property alias radius: dialog.radius
+    property alias border: dialog.border
+
+    // error texts
+    property string noMatchWarning: qsTr("Passwords do not match!")
+    property string samePwdWarning: qsTr("Password same as before. Please change!")
+    property string pwdEmptyWarning: qsTr("Empty password. Please enter password!")
+
+    signal ok // password input completed
+    signal cancel // dialog canceled
+    signal error(string msg) // invalid prompts
+
+    // set prompts and show dialog
+    function show(newPwdTxt, repeatPwdTxt) {
+        if(newPwdTxt != "" && repeatPwdTxt != "")
+        {
+            prompt1Txt.text = newPwdTxt
+            prompt2Txt.text = repeatPwdTxt
+            container.visible = true
+        } else {
+            // todo: prompts unknown (new/repeat password)
+            error("unknown pam message prompts!\n" +
+                     "prompt1=" + newPwdTxt +
+                     "prompt2=" + repeatPwdTxt)
+        }
+    }
+
+    // clear all text
+    function clear() {
+       infoarea.text = ""
+       pwd1Input.text = ""
+       pwd2Input.text = ""
+    }
+
+    // append new pam error/info
+    function append(msg) {
+        infoarea.append(msg)
+    }
+
+    // internal
+
+    function resetEmptyWarning() {
+        emptyWarningShown = false
+    }
+
+    // flag: warning about empty password already shown?
+    property bool emptyWarningShown: false
+    // previously typed password (refused by pam)
+    property string previousPwd: ""
+
+    function validatePwds() {
+        if(pwd1Input.text.length == 0 ||
+           pwd2Input.text.length == 0)
+        {
+            if(emptyWarningShown==false)
+            {
+                infoarea.append(pwdEmptyWarning)
+                emptyWarningShown = true
+            }
+            return
+        }
+
+        if(pwd1Input.text.length>0)
+        {
+            resetEmptyWarning()
+
+            if(pwd1Input.text != pwd2Input.text) {
+                infoarea.append(noMatchWarning)
+                return
+            }
+
+            if(previousPwd == pwd1Input.text) {
+                // if pam refuses new password (e.g. too similar)
+                // and this is presented to pam several times pam
+                // seems pissed off and stops conversation, so avoid
+                infoarea.append(samePwdWarning)
+                return
+            }
+
+            // new password ok
+            previousPwd = pwd1Input.text
+            ok()
+        }
+    }
+
+    onVisibleChanged: {
+        if(visible == true)
+            pwd1Input.text = ""
+            pwd2Input.text = ""
+            pwd1Input.focus = true
+            focus = true
+    }
+
+    Rectangle {
+        id: dialog
+        anchors.fill: parent
+
+        GridLayout {
+            id: contentGrid
+            anchors.fill: parent
+            columns: 2
+
+            Item {
+                width: infosWidth
+                height: infosHeight+36
+                Layout.columnSpan: 2
+                Layout.minimumWidth: infosWidth
+                Layout.margins: margins
+
+                // show info/error from pam conv()
+                TextArea {
+                    id: infoarea
+                    anchors.fill: parent
+                    viewport.implicitHeight: infosHeight
+                    backgroundVisible: false
+                    activeFocusOnPress: false
+                    readOnly: true
+                    textColor: infosColor
+                    frameVisible: false
+                    verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
+                }
+            }
+
+            // shows message (prompt) from pam conv()
+            Text {
+                id: prompt1Txt
+                Layout.minimumWidth: 64
+                Layout.alignment: Qt.AlignRight
+                color: promptColor
+                text: qsTr("New password:")
+            }
+
+            PasswordBox {
+                id: pwd1Input
+                implicitWidth: 150
+                maximumLength: 64
+                KeyNavigation.down: pwd2Input
+                KeyNavigation.tab: pwd2Input
+                Keys.onEnterPressed:pwd2Input.forceActiveFocus()
+                Keys.onReturnPressed: pwd2Input.forceActiveFocus()
+                onAccepted: {
+                    if(pwd1Input.text != "" && pwd2Input.text != "" )
+                        validatePwds()
+                }
+            }
+
+            // shows message (prompt) from pam conv()
+            Text {
+                id: prompt2Txt
+                Layout.minimumWidth: 64
+                Layout.alignment: Qt.AlignRight
+                color: promptColor
+                text: qsTr("Repeat password:")
+            }
+
+            PasswordBox {
+                id: pwd2Input
+                implicitWidth: 150
+                maximumLength: 64
+                KeyNavigation.down: cancelBtn
+                KeyNavigation.tab: cancelBtn
+                Keys.onEnterPressed: if(pwd1Input == "") pwd1Input.forceActiveFocus()
+                Keys.onReturnPressed: if(pwd1Input == "") pwd1Input.forceActiveFocus()
+                onAccepted: validatePwds()
+            }
+
+            RowLayout {
+
+                Layout.columnSpan: 2
+                Layout.bottomMargin: 32
+                Layout.topMargin: margins
+                Layout.fillWidth: true
+                Layout.alignment: Qt.AlignCenter
+                spacing: 8
+
+                Button {
+                    id: cancelBtn
+                    text: qsTr("Cancel")
+                    activeFocusOnTab : true
+                    Keys.onEnterPressed: clicked()
+                    Keys.onReturnPressed: clicked()
+                    KeyNavigation.right: resetBtn
+                    onClicked: {
+                        container.visible = false
+                        resetEmptyWarning()
+                        cancel()
+                    }
+                }
+
+                Button {
+                    id: resetBtn
+                    text: qsTr("Reset")
+                    activeFocusOnTab : true
+                    enabled: pwd1Input.text != "" || pwd2Input.text != ""
+                    Keys.onEnterPressed: clicked()
+                    Keys.onReturnPressed: clicked()
+                    KeyNavigation.right: okBtn
+                    KeyNavigation.left: cancelBtn
+                    KeyNavigation.up: pwd2Input
+                    onClicked: {
+                        pwd1Input.text = ""
+                        pwd2Input.text = ""
+                        resetEmptyWarning()
+                        pwd1Input.forceActiveFocus()
+                    }
+                }
+
+                Button {
+                    id: okBtn
+                    text: qsTr("Ok")
+                    activeFocusOnTab : true
+                    enabled: pwd1Input.text != "" && pwd2Input.text != ""
+                    Keys.onEnterPressed: clicked()
+                    Keys.onReturnPressed: clicked()
+                    KeyNavigation.left: resetBtn
+                    KeyNavigation.tab: pwd1Input
+                    KeyNavigation.up: pwd2Input
+                    onClicked: validatePwds()
+                }
+            }
+        }
+    }
+}
diff --git a/components/2.0/TextBox.qml b/components/2.0/TextBox.qml
index a8d89af..c452738 100644
--- a/components/2.0/TextBox.qml
+++ b/components/2.0/TextBox.qml
@@ -37,6 +37,10 @@ FocusScope {
     property alias textColor: txtMain.color
     property alias echoMode: txtMain.echoMode
     property alias text: txtMain.text
+    property alias validator: txtMain.validator
+    property alias maximumLength: txtMain.maximumLength
+
+    signal accepted
 
     Rectangle {
         id: main
@@ -85,5 +89,7 @@ FocusScope {
         focus: true
 
         passwordCharacter: "\u25cf"
+
+        onAccepted: container.accepted()
     }
 }
--- a/components/common/qmldir
+++ b/components/common/qmldir
@@ -8,6 +8,8 @@ ImageButton 2.0 ImageButton.qml
 LayoutBox 2.0 LayoutBox.qml
 Menu 2.0 Menu.qml
 PasswordBox 2.0 PasswordBox.qml
+PasswordRenewal 2.0 PasswordRenewal.qml
+PasswordConnections 2.0 PasswordConnections.qml
 PictureBox 2.0 PictureBox.qml
 TextBox 2.0 TextBox.qml
 TextConstants 2.0 TextConstants.qml
diff --git a/data/themes/elarun/Main.qml b/data/themes/elarun/Main.qml
index 37a97a0..a943e87 100644
--- a/data/themes/elarun/Main.qml
+++ b/data/themes/elarun/Main.qml
@@ -35,13 +35,13 @@ Rectangle {
 
     TextConstants { id: textConstants }
 
-    Connections {
-        target: sddm
-        onLoginSucceeded: {
-        }
-        onLoginFailed: {
-            pw_entry.text = ""
-        }
+    // container for password renewal logic
+    PasswordConnections {
+        renewalDialog: renewal
+        pwdItem: pw_entry // use PasswordBox.text
+        getsBackFocus: pw_entry
+        //errMsg: none
+        //txtMsg: none
     }
 
     Background {
@@ -60,9 +60,22 @@ Rectangle {
         color: "transparent"
         //visible: primaryScreen
 
+        PasswordRenewal {
+            id: renewal
+            anchors.horizontalCenter: rectangle.horizontalCenter
+            anchors.top: rectangle.bottom
+            anchors.topMargin: 32
+            visible: false
+            radius: 8
+            color: "#22888888"
+            infosColor: "lightcoral"
+        }
+
         Rectangle {
+            id: rectangle
             width: 416; height: 262
             color: "#00000000"
+            enabled: !renewal.visible
 
             anchors.centerIn: parent
 
diff --git a/data/themes/maldives/Main.qml b/data/themes/maldives/Main.qml
index 00feea4..286ad80 100644
--- a/data/themes/maldives/Main.qml
+++ b/data/themes/maldives/Main.qml
@@ -35,19 +35,13 @@ Rectangle {
 
     TextConstants { id: textConstants }
 
-    Connections {
-        target: sddm
-
-        onLoginSucceeded: {
-            errorMessage.color = "steelblue"
-            errorMessage.text = textConstants.loginSucceeded
-        }
-
-        onLoginFailed: {
-            password.text = ""
-            errorMessage.color = "red"
-            errorMessage.text = textConstants.loginFailed
-        }
+    // container for password renewal logic
+    PasswordConnections {
+        renewalDialog: renewal
+        pwdItem: password // use PasswordBox.text
+        getsBackFocus: password
+        errMsg: errorMessage
+        txtMsg: errorMessage
     }
 
     Background {
@@ -76,10 +70,31 @@ Rectangle {
         }
 
         Image {
+            id: renewalImg
+            anchors.top: parent.top
+            anchors.left: parent.left
+            anchors.margins: 64
+            width: renewal.width+32
+            height: renewal.height+32
+            visible: renewal.visible
+
+            source: "rectangle.png"
+        }
+
+        PasswordRenewal {
+            id: renewal
+            anchors.centerIn: renewalImg
+            visible: false
+            radius: 8
+            color: "transparent"
+        }
+
+        Image {
             id: rectangle
             anchors.centerIn: parent
             width: Math.max(320, mainColumn.implicitWidth + 50)
             height: Math.max(320, mainColumn.implicitHeight + 50)
+            enabled: !renewal.visible
 
             source: "rectangle.png"
 
diff --git a/docs/EXPIRED-PASSWORD.md b/docs/EXPIRED-PASSWORD.md
new file mode 100755
index 0000000..e988488
--- /dev/null
+++ b/docs/EXPIRED-PASSWORD.md
@@ -0,0 +1,83 @@
+## Introduction
+
+SDDM can handle expired passwords during login.
+A password renewal dialog is provided to change the expired password.
+The greeter frontend will talk with the backend to handle the pam conversation.
+Support for password renewal is provided with two components used in greeter themes:
+
+* ``PasswordRenewal.qml`` basic dialog for password renewal, for password input and confirmation
+* ``PasswordConnections.qml`` container for Connections, hides signal handling from themes
+
+## Usage
+
+For new themes the two components have to be included in the ``Main.qml``.
+See the built-in greeter theme ``Main.qml`` for an example,
+and add this to  ``Main.qml``:
+
+```
+// container with password renewal logic
+PasswordConnections {
+    renewalDialog: renewal // PasswordRenewal dialog id (see below)
+    pwdItem: listView.currentItem // gets password input from currentItem.password
+    getsBackFocus: listView // item where focus falls back after dialog closes
+    errMsg: errMessage // if set defines (text) item which shows errors
+    txtMsg: txtMessage // if set (text) item which shows pam infos
+}
+
+...
+
+        Item {
+            id: usersContainer
+            // block other input during password renewal
+            enabled: !renewal.visible
+
+...
+
+            // password renewal dialog
+            PasswordRenewal {
+                id: renewal
+                // customize here e.g.:
+                //anchors.horizontalCenter: parent.horizontalCenter
+                //anchors.bottom: usersContainer.top
+                //visible: false
+                //color: "#22888888"
+                //promptColor: "white"
+                //infosColor: "lightcoral"
+            }
+```
+
+## Some more details
+
+The following qml objects are available in ``Main.qml``,
+they are used for password renewal and pam conversation:
+
+### Signals
+
+Signals coming from daemon backend:
+
+* ``pamConvMsg(pam_msg)``
+Provides infos/errors from (pam) backend conversation to present to user.
+
+* ``pamRequest()``
+New request from (pam) backend, user response is required.
+
+### Property
+
+* ``request`` (type AuthRequest)
+Prompts from (pam) backend with messages from pam_conv.
+
+### Response
+
+Responses from greeter frontend to the backend:
+
+* ``sddm.enablePwdRenewal()``
+The theme tells the greeter it can handle password renewal (has a dialog and logic as described above).
+For themes which do not have a password renewal dialog, this method is not called.
+In that case the pam conversation is just canceled (otherwise pam_conv user session sits there waiting for password response).
+This will keep compatibility to (older) themes which do not support expired passwords yet.
+
+* ``sddm.pamResponse(password)``
+Send password response to (pam) backend, i.e. pam_conv.
+
+* ``sddm.cancelPamConv()``
+Cancel pam conversation with pam_conv.
diff --git a/src/auth/Auth.cpp b/src/auth/Auth.cpp
index d5dbca5..9bf3bb9 100644
--- a/src/auth/Auth.cpp
+++ b/src/auth/Auth.cpp
@@ -58,6 +58,7 @@ namespace SDDM {
         void dataPending();
         void childExited(int exitCode, QProcess::ExitStatus exitStatus);
         void childError(QProcess::ProcessError error);
+        void cancelPamConv();
         void requestFinished();
     public:
         AuthRequest *request { nullptr };
@@ -133,6 +134,7 @@ namespace SDDM {
         child->setProcessEnvironment(env);
         connect(child, QOverload<int,QProcess::ExitStatus>::of(&QProcess::finished), this, &Auth::Private::childExited);
         connect(child, QOverload<QProcess::ProcessError>::of(&QProcess::error), this, &Auth::Private::childError);
+        connect(request, &AuthRequest::canceled, this, &Auth::Private::cancelPamConv);
         connect(request, &AuthRequest::finished, this, &Auth::Private::requestFinished);
         connect(request, &AuthRequest::promptsChanged, parent, &Auth::requestChanged);
     }
@@ -148,61 +150,67 @@ namespace SDDM {
         connect(socket, &QLocalSocket::readyRead, this, &Auth::Private::dataPending);
     }
 
+    // from (PAM) Backend to daemon
     void Auth::Private::dataPending() {
         Auth *auth = qobject_cast<Auth*>(parent());
         Msg m = MSG_UNKNOWN;
         SafeDataStream str(socket);
-        str.receive();
-        str >> m;
-        switch (m) {
-            case ERROR: {
-                QString message;
-                Error type = ERROR_NONE;
-                str >> message >> type;
-                Q_EMIT auth->error(message, type);
-                break;
-            }
-            case INFO: {
-                QString message;
-                Info type = INFO_NONE;
-                str >> message >> type;
-                Q_EMIT auth->info(message, type);
-                break;
-            }
-            case REQUEST: {
-                Request r;
-                str >> r;
-                request->setRequest(&r);
-                break;
-            }
-            case AUTHENTICATED: {
-                QString user;
-                str >> user;
-                if (!user.isEmpty()) {
-                    auth->setUser(user);
-                    Q_EMIT auth->authentication(user, true);
+        do {
+            str.receive();
+            str >> m;
+            switch (m) {
+                case ERROR: {
+                    QString message;
+                    Error type = ERROR_NONE;
+                    str >> message >> type;
+                    Q_EMIT auth->error(message, type);
+                    break;
+                }
+                case INFO: {
+                    QString message;
+                    Info type = INFO_NONE;
+                    str >> message >> type;
+                    Q_EMIT auth->info(message, type);
+                    break;
+                }
+                // request from (PAM) Backend
+                case REQUEST: {
+                    Request r;
+                    str >> r;
+                    request->setRequest(&r);
+                    qDebug() << "Auth: Request received";
+                    break;
+                }
+                case AUTHENTICATED: {
+                    QString user;
+                    str >> user;
+                    if (!user.isEmpty()) {
+                        auth->setUser(user);
+                        Q_EMIT auth->authentication(user, true);
+                        str.reset();
+                        str << AUTHENTICATED << environment << cookie;
+                        str.send();
+                    }
+                    else {
+                        Q_EMIT auth->authentication(user, false);
+                    }
+                    break;
+                }
+                case SESSION_STATUS: {
+                    bool status;
+                    str >> status;
+                    Q_EMIT auth->sessionStarted(status);
                     str.reset();
-                    str << AUTHENTICATED << environment << cookie;
+                    str << SESSION_STATUS;
                     str.send();
+                    break;
                 }
-                else {
-                    Q_EMIT auth->authentication(user, false);
+                default: {
+                    qWarning("Auth: dataPending:  Unknown message received: %d", m);
+                    Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL);
                 }
-                break;
-            }
-            case SESSION_STATUS: {
-                bool status;
-                str >> status;
-                Q_EMIT auth->sessionStarted(status);
-                str.reset();
-                str << SESSION_STATUS;
-                str.send();
-                break;
             }
-            default: {
-                Q_EMIT auth->error(QStringLiteral("Auth: Unexpected value received: %1").arg(m), ERROR_INTERNAL);
-            }
-        }
+        } while(str.available());
     }
 
     void Auth::Private::childExited(int exitCode, QProcess::ExitStatus exitStatus) {
@@ -224,6 +232,16 @@ namespace SDDM {
         Q_EMIT qobject_cast<Auth*>(parent())->error(child->errorString(), ERROR_INTERNAL);
     }
 
+    // from daemon to (PAM) backend
+    void Auth::Private::cancelPamConv() {
+        SafeDataStream str(socket);
+        qDebug() << "Auth: cancelPamConv, send CANCEL to backend";
+        str << CANCEL;
+        str.send();
+        request->setRequest();
+    }
+
+    // from daemon to (PAM) backend
     void Auth::Private::requestFinished() {
         SafeDataStream str(socket);
         Request r = request->request();
@@ -232,7 +250,6 @@ namespace SDDM {
         request->setRequest();
     }
 
-
     Auth::Auth(const QString &user, const QString &session, bool autologin, QObject *parent, bool verbose)
             : QObject(parent)
             , d(new Private(this)) {
diff --git a/src/auth/Auth.h b/src/auth/Auth.h
index 9c26b95..cb7a736 100644
--- a/src/auth/Auth.h
+++ b/src/auth/Auth.h
@@ -21,8 +21,8 @@
 #ifndef SDDM_AUTH_H
 #define SDDM_AUTH_H
 
-#include "AuthRequest.h"
 #include "AuthPrompt.h"
+#include "AuthRequest.h"
 
 #include <QtCore/QObject>
 #include <QtCore/QProcessEnvironment>
@@ -66,6 +66,7 @@ namespace SDDM {
         enum Info {
             INFO_NONE = 0,
             INFO_UNKNOWN,
+            INFO_PAM_CONV,
             INFO_PASS_CHANGE_REQUIRED,
             _INFO_LAST
         };
diff --git a/src/auth/AuthBase.h b/src/auth/AuthBase.h
new file mode 100644
index 0000000..ca09864
--- /dev/null
+++ b/src/auth/AuthBase.h
@@ -0,0 +1,137 @@
+/*
+ * PAM request and message IDs shared between greeter, daemon, helper
+ * Copyright (C) 2013 Martin Bе≥ц╜za <mbriza@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef AUTHBASE_H
+#define AUTHBASE_H
+
+#include <QtCore/QDataStream>
+
+// AuthPrompt::Type
+#include "AuthPrompt.h"
+
+namespace SDDM {
+    class Prompt {
+    public:
+        Prompt() { }
+        Prompt(AuthPrompt::Type type, QString message, bool hidden)
+                : type(type), message(message), hidden(hidden) { }
+        Prompt(const Prompt &o)
+                : type(o.type), response(o.response), message(o.message), hidden(o.hidden) { }
+        ~Prompt() {
+            clear();
+        }
+        Prompt& operator=(const Prompt &o) {
+            type = o.type;
+            response = o.response;
+            message = o.message;
+            hidden = o.hidden;
+            return *this;
+        }
+        bool operator==(const Prompt &o) const {
+            return type == o.type && response == o.response && message == o.message && hidden == o.hidden;
+        }
+        bool valid() const {
+            return !(type == AuthPrompt::NONE && response.isEmpty() && message.isEmpty());
+        }
+        void clear() {
+            type = AuthPrompt::NONE;
+            // overwrite the whole thing with zeroes before clearing
+            memset(response.data(), 0, response.length());
+            response.clear();
+            message.clear();
+            hidden = false;
+        }
+
+        AuthPrompt::Type type { AuthPrompt::NONE };
+        QByteArray response { };
+        QString message { };
+        bool hidden { false };
+    };
+
+    class Request {
+    public:
+        Request() { }
+        Request(QList<Prompt> prompts)
+                : prompts(prompts) { }
+        Request(const Request &o)
+                : prompts(o.prompts) { }
+        Request& operator=(const Request &o) {
+            prompts = QList<Prompt>(o.prompts);
+            return *this;
+        }
+        bool operator==(const Request &o) const {
+            return prompts == o.prompts;
+        }
+        bool valid() const {
+            return !(prompts.isEmpty());
+        }
+        void clear() {
+            prompts.clear();
+        }
+
+        QList<Prompt> prompts { };
+    };
+
+    inline QDataStream& operator<<(QDataStream &s, const Prompt &m) {
+        s << qint32(m.type) << m.message << m.hidden << m.response;
+        return s;
+    }
+
+    inline QDataStream& operator>>(QDataStream &s, Prompt &m) {
+        qint32 type;
+        QString message;
+        bool hidden;
+        QByteArray response;
+        s >> type >> message >> hidden >> response;
+        m.type = AuthPrompt::Type(type);
+        m.message = message;
+        m.hidden = hidden;
+        m.response = response;
+        return s;
+    }
+
+    inline QDataStream& operator<<(QDataStream &s, const Request &m) {
+        qint32 length = m.prompts.length();
+        s << length;
+        for(const Prompt &p : qAsConst(m.prompts)) {
+            s << p;
+        }
+        return s;
+    }
+
+    inline QDataStream& operator>>(QDataStream &s, Request &m) {
+        QList<Prompt> prompts;
+        qint32 length;
+        s >> length;
+        for (int i = 0; i < length; i++) {
+            Prompt p;
+            s >> p;
+            prompts << p;
+        }
+        if (prompts.length() != length) {
+            s.setStatus(QDataStream::ReadCorruptData);
+            return s;
+        }
+        m.prompts = prompts;
+        return s;
+    }
+}
+
+#endif // AUTHBASE_H
diff --git a/src/auth/AuthMessages.h b/src/auth/AuthMessages.h
index ec16d00..00dd714 100644
--- a/src/auth/AuthMessages.h
+++ b/src/auth/AuthMessages.h
@@ -26,68 +26,10 @@
 
 #include "Auth.h"
 
-namespace SDDM {
-    class Prompt {
-    public:
-        Prompt() { }
-        Prompt(AuthPrompt::Type type, QString message, bool hidden)
-                : type(type), message(message), hidden(hidden) { }
-        Prompt(const Prompt &o)
-                : type(o.type), response(o.response), message(o.message), hidden(o.hidden) { }
-        ~Prompt() {
-            clear();
-        }
-        Prompt& operator=(const Prompt &o) {
-            type = o.type;
-            response = o.response;
-            message = o.message;
-            hidden = o.hidden;
-            return *this;
-        }
-        bool operator==(const Prompt &o) const {
-            return type == o.type && response == o.response && message == o.message && hidden == o.hidden;
-        }
-        bool valid() const {
-            return !(type == AuthPrompt::NONE && response.isEmpty() && message.isEmpty());
-        }
-        void clear() {
-            type = AuthPrompt::NONE;
-            // overwrite the whole thing with zeroes before clearing
-            memset(response.data(), 0, response.length());
-            response.clear();
-            message.clear();
-            hidden = false;
-        }
-
-        AuthPrompt::Type type { AuthPrompt::NONE };
-        QByteArray response { };
-        QString message { };
-        bool hidden { false };
-    };
+// Request and Prompt classes
+#include "AuthBase.h"
 
-    class Request {
-    public:
-        Request() { }
-        Request(QList<Prompt> prompts)
-                : prompts(prompts) { }
-        Request(const Request &o)
-                : prompts(o.prompts) { }
-        Request& operator=(const Request &o) {
-            prompts = QList<Prompt>(o.prompts);
-            return *this;
-        }
-        bool operator==(const Request &o) const {
-            return prompts == o.prompts;
-        }
-        bool valid() const {
-            return !(prompts.isEmpty());
-        }
-        void clear() {
-            prompts.clear();
-        }
-
-        QList<Prompt> prompts { };
-    };
+namespace SDDM {
 
     enum Msg {
         MSG_UNKNOWN = 0,
@@ -95,6 +37,7 @@ namespace SDDM {
         ERROR,
         INFO,
         REQUEST,
+        CANCEL,
         AUTHENTICATED,
         SESSION_STATUS,
         MSG_LAST,
@@ -166,49 +109,6 @@ namespace SDDM {
         return s;
     }
 
-    inline QDataStream& operator<<(QDataStream &s, const Prompt &m) {
-        s << qint32(m.type) << m.message << m.hidden << m.response;
-        return s;
-    }
-
-    inline QDataStream& operator>>(QDataStream &s, Prompt &m) {
-        qint32 type;
-        QString message;
-        bool hidden;
-        QByteArray response;
-        s >> type >> message >> hidden >> response;
-        m.type = AuthPrompt::Type(type);
-        m.message = message;
-        m.hidden = hidden;
-        m.response = response;
-        return s;
-    }
-
-    inline QDataStream& operator<<(QDataStream &s, const Request &m) {
-        qint32 length = m.prompts.length();
-        s << length;
-        for(const Prompt &p : qAsConst(m.prompts)) {
-            s << p;
-        }
-        return s;
-    }
-
-    inline QDataStream& operator>>(QDataStream &s, Request &m) {
-        QList<Prompt> prompts;
-        qint32 length;
-        s >> length;
-        for (int i = 0; i < length; i++) {
-            Prompt p;
-            s >> p;
-            prompts << p;
-        }
-        if (prompts.length() != length) {
-            s.setStatus(QDataStream::ReadCorruptData);
-            return s;
-        }
-        m.prompts = prompts;
-        return s;
-    }
 }
 
 #endif // MESSAGES_H
diff --git a/src/auth/AuthPrompt.cpp b/src/auth/AuthPrompt.cpp
index 7434ede..96b8034 100644
--- a/src/auth/AuthPrompt.cpp
+++ b/src/auth/AuthPrompt.cpp
@@ -19,8 +19,8 @@
  */
 
 #include "AuthPrompt.h"
-#include "Auth.h"
-#include "AuthMessages.h"
+#include "AuthBase.h"
+#include "AuthRequest.h"
 
 namespace SDDM {
     class AuthPrompt::Private : public Prompt {
@@ -69,4 +69,37 @@ namespace SDDM {
     bool AuthPrompt::hidden() const {
         return d->hidden;
     }
+
+    // @internal for debug logging purposes
+    const QString &AuthPrompt::typeToString(int type) {
+
+        static const QString stringList[] = {
+            QStringLiteral("NONE"),
+            QStringLiteral("UNKNOWN"),
+            QStringLiteral("CHANGE_CURRENT"),
+            QStringLiteral("CHANGE_NEW"),
+            QStringLiteral("CHANGE_REPEAT"),
+            QStringLiteral("LOGIN_USER"),
+            QStringLiteral("LOGIN_PASSWORD"),
+        };
+
+        switch(type) {
+            case AuthPrompt::NONE:
+                return stringList[0]; break;
+            case AuthPrompt::CHANGE_CURRENT:
+                return stringList[2]; break;
+            case AuthPrompt::CHANGE_NEW:
+                return stringList[3]; break;
+            case AuthPrompt::CHANGE_REPEAT:
+                return stringList[4]; break;
+            case AuthPrompt::LOGIN_USER:
+                return stringList[5]; break;
+            case AuthPrompt::LOGIN_PASSWORD:
+                return stringList[6]; break;
+            default: break;
+        }
+
+        // AuthPrompt::UNKNOWN
+        return stringList[1];
+    }
 }
diff --git a/src/auth/AuthPrompt.h b/src/auth/AuthPrompt.h
index 8ed394d..2f1b0f0 100644
--- a/src/auth/AuthPrompt.h
+++ b/src/auth/AuthPrompt.h
@@ -24,7 +24,6 @@
 #include <QtCore/QObject>
 
 namespace SDDM {
-    class Auth;
     class AuthRequest;
     class Prompt;
     /**
@@ -85,6 +84,12 @@ namespace SDDM {
         * @param r data entered by the user
         */
         void setResponse(const QByteArray &r);
+        /**
+         * @brief get string representation of AuthPrompt type, see enum @ref Type
+         * @note for debug logging
+         */
+        static const QString &typeToString(int type);
+
     Q_SIGNALS:
         /**
         * Emitted when the response was entered by the user
@@ -99,4 +104,4 @@ namespace SDDM {
     };
 }
 
-#endif //PROMPT_H
\ No newline at end of file
+#endif //PROMPT_H
diff --git a/src/auth/AuthRequest.cpp b/src/auth/AuthRequest.cpp
index 2c4764a..0d82503 100644
--- a/src/auth/AuthRequest.cpp
+++ b/src/auth/AuthRequest.cpp
@@ -19,7 +19,6 @@
  */
 
 #include "AuthRequest.h"
-#include "Auth.h"
 #include "AuthMessages.h"
 
 namespace SDDM {
@@ -46,11 +45,11 @@ namespace SDDM {
             qobject_cast<AuthRequest*>(parent())->done();
     }
 
-    AuthRequest::AuthRequest(Auth *parent)
+    AuthRequest::AuthRequest(QObject *parent)
             : QObject(parent)
             , d(new Private(this)) { }
 
-    void AuthRequest::setRequest(const Request *request) {
+    void AuthRequest::setRequest(const Request * const request) {
         QList<AuthPrompt*> promptsCopy(d->prompts);
         d->prompts.clear();
         if (request != nullptr) {
@@ -72,7 +71,7 @@ namespace SDDM {
         return d->prompts;
     }
 
-    QQmlListProperty<AuthPrompt> AuthRequest::promptsDecl() {
+    QQmlListProperty<AuthPrompt> AuthRequest::promptsRead() {
         return QQmlListProperty<AuthPrompt>(this, d->prompts);
     }
 
@@ -83,6 +82,14 @@ namespace SDDM {
         }
     }
 
+    // send to (pam) backend
+    void AuthRequest::cancel() {
+        if (!d->finished) {
+            d->finished = true;
+        }
+        Q_EMIT canceled();
+    }
+
     bool AuthRequest::finishAutomatically() {
         return d->finishAutomatically;
     }
@@ -106,6 +113,47 @@ namespace SDDM {
         }
         return r;
     }
+
+    QString AuthRequest::findNewPwdMessage() {
+        for(const AuthPrompt *qap : qAsConst(d->prompts)) {
+            if(qap->type()==AuthPrompt::CHANGE_NEW)
+                return qap->message();
+        }
+        return QString();
+    }
+
+    QString AuthRequest::findRepeatPwdMessage() {
+        for(const AuthPrompt *qap : qAsConst(d->prompts)) {
+            if(qap->type()==AuthPrompt::CHANGE_REPEAT)
+                return qap->message();
+        }
+        return QString();
+    }
+
+    AuthPrompt *AuthRequest::findPrompt(AuthPrompt::Type type) const {
+        for(AuthPrompt *qap : d->prompts) {
+            if(qap->type()==type)
+                return qap;
+        }
+        return NULL;
+    }
+
+    void AuthRequest::setChangeResponse(const QString &currentPwd, const QString &newPassword) {
+        AuthPrompt* prompt;
+
+        if(!currentPwd.isNull()) {
+            prompt = findPrompt(AuthPrompt::CHANGE_CURRENT);
+            if(prompt) prompt->setResponse(qPrintable(currentPwd));
+        }
+
+        if(!newPassword.isNull()) {
+            prompt = findPrompt(AuthPrompt::CHANGE_NEW);
+            if(prompt) prompt->setResponse(qPrintable(newPassword));
+
+            prompt = findPrompt(AuthPrompt::CHANGE_REPEAT);
+            if(prompt) prompt->setResponse(qPrintable(newPassword));
+        }
+    }
 }
 
 #include "AuthRequest.moc"
diff --git a/src/auth/AuthRequest.h b/src/auth/AuthRequest.h
index b8ef43c..113fd10 100644
--- a/src/auth/AuthRequest.h
+++ b/src/auth/AuthRequest.h
@@ -23,11 +23,12 @@
 
 #include <QtCore/QObject>
 
+#include <AuthPrompt.h>
+
 #include <QtQml/QQmlListProperty>
 
 namespace SDDM {
-    class Auth;
-    class AuthPrompt;
+    //class AuthPrompt;
     class Request;
     /**
     * \brief
@@ -48,12 +49,28 @@ namespace SDDM {
     *
     * \todo Decide if it's sane to use the info messages from PAM or to somehow parse them
     * and make the password changing message into a Request::Type of some kind
+    *
+    *  \note AuthRequest (and AuthPrompt) is mainly used in (Pam)Backend
+     * and closely bundled with the Auth.cpp parent which handles user session
+     * after authentication etc.
+     *
+     * But AuthRequest is also secondly used as pure (pam_conv) message container in greeter
+     * without the Auth parent overhead (but still with the signal/slot amenities) - depending on
+     * the constructor used. In this case it just hands over the pam_conv() messages and possibly
+     * user (password) responses between daemon, greeter and qml.
     */
     class AuthRequest : public QObject {
         Q_OBJECT
-        Q_PROPERTY(QQmlListProperty<AuthPrompt> prompts READ promptsDecl NOTIFY promptsChanged)
+        Q_PROPERTY(QQmlListProperty<SDDM::AuthPrompt> prompts READ promptsRead NOTIFY promptsChanged)
         Q_PROPERTY(bool finishAutomatically READ finishAutomatically WRITE setFinishAutomatically NOTIFY finishAutomaticallyChanged)
+
     public:
+        /** @brief for AuthRequest without using Auth parent.
+         *
+         * when used as parameter container to hand over pam request (prompts with pam
+         * messages and user pwd responses) between daemon and greeter (proxy) to qml view.
+         */
+        AuthRequest(QObject *parent = 0);
         /**
         * @return list of the contained prompts
         */
@@ -62,30 +79,68 @@ namespace SDDM {
         * For QML apps
         * @return list of the contained prompts
         */
-        QQmlListProperty<AuthPrompt> promptsDecl();
+        QQmlListProperty<AuthPrompt> promptsRead();
+        /**
+         * @brief Find pam message of type CHANGE_NEW in prompts list
+         * @return pam message string
+         */
+        Q_INVOKABLE QString findNewPwdMessage();
+        /**
+         * @brief Find pam message of type CHANGE_REPEAT in prompts list
+         * @return pam message string
+         */
+        Q_INVOKABLE QString findRepeatPwdMessage();
+        /**
+          * @brief Write password responses into request for pam conv(),
+          * for AuthPrompt::CHANGE_NEW, CHANGE_REPEAT, CHANGE_CURRENT
+          * @param currentPwd Current user password (expired password)
+          * @param newPassword New user password (replaces expired password)
+          */
+        Q_INVOKABLE void setChangeResponse(const QString &currentPwd, const QString &newPassword);
 
         static AuthRequest *empty();
 
         bool finishAutomatically();
+        /**
+         * @brief Trigger slot done() automaticly when all AuthPrompt responses (user passwords) are set.
+         * @param value  if true done() automaticly called, if false user calls done() after specifying all responses
+         */
         void setFinishAutomatically(bool value);
+        /**
+         * @brief Fill AuthRequest with Prompts from Request
+         * @param Request Prompts with possible responses
+         */
+        void setRequest(const Request * const request = nullptr);
+        /**
+         * @brief convert AuthRequest to simple Request type
+         * @return Request with Prompts
+         */
+        Request request() const;
+        /**
+         * @brief find prompt with specified type
+         * @param type \ref AuthPrompt::Type
+         * @return pointer to prompt of that type
+         */
+        AuthPrompt *findPrompt(AuthPrompt::Type type) const;
+
     public Q_SLOTS:
         /**
-        * Call this slot when all prompts has been filled to your satisfaction
+        * Call this slot when all prompts has been filled (with responses) to your satisfaction
         */
         void done();
+        /**
+        * Call this slot when user canceled password dialog
+        */
+        void cancel();
     Q_SIGNALS:
         /**
         * Emitted when \ref done was called
         */
         void finished();
-
+        void canceled();
         void finishAutomaticallyChanged();
         void promptsChanged();
-    private:
-        AuthRequest(Auth *parent);
-        void setRequest(const Request *request = nullptr);
-        Request request() const;
-        friend class Auth;
+    protected:
         class Private;
         Private *d { nullptr };
     };
diff --git a/src/common/Messages.h b/src/common/Messages.h
index c779791..a587bb4 100644
--- a/src/common/Messages.h
+++ b/src/common/Messages.h
@@ -23,9 +23,12 @@
 #include <QFlags>
 
 namespace SDDM {
+    /** daemon to greeter messages */
     enum class GreeterMessages {
         Connect = 0,
         Login,
+        PamResponse,
+        PamCancel,
         PowerOff,
         Reboot,
         Suspend,
@@ -33,9 +36,12 @@ namespace SDDM {
         HybridSleep
     };
 
+    /** greeter to daemon messages */
     enum class DaemonMessages {
         HostName,
         Capabilities,
+        PamConvMsg,
+        PamRequest,
         LoginSucceeded,
         LoginFailed
     };
diff --git a/src/common/SafeDataStream.cpp b/src/common/SafeDataStream.cpp
index f62a074..332e6a0 100644
--- a/src/common/SafeDataStream.cpp
+++ b/src/common/SafeDataStream.cpp
@@ -74,6 +74,15 @@ namespace SDDM {
         }
     }
 
+    bool SafeDataStream::available() {
+        if (!m_device->isOpen()) {
+            qCritical() << " Auth: SafeDataStream: Could not read from the device";
+            return false;
+        }
+
+        return m_device->bytesAvailable();
+    }
+
     void SafeDataStream::reset() {
         m_data.clear();
         device()->reset();
diff --git a/src/common/SafeDataStream.h b/src/common/SafeDataStream.h
index 8bbbe8e..1f77920 100644
--- a/src/common/SafeDataStream.h
+++ b/src/common/SafeDataStream.h
@@ -29,6 +29,7 @@ namespace SDDM {
         SafeDataStream(QIODevice* device);
         void send();
         void receive();
+        bool available();
         void reset();
 
     private:
diff --git a/src/common/SocketWriter.cpp b/src/common/SocketWriter.cpp
index 32db235..3f8d0cf 100644
--- a/src/common/SocketWriter.cpp
+++ b/src/common/SocketWriter.cpp
@@ -49,4 +49,10 @@ namespace SDDM {
 
         return *this;
     }
+
+    SocketWriter &SocketWriter::operator << (const Request &r) {
+        *output << r;
+
+        return *this;
+    }
 }
diff --git a/src/common/SocketWriter.h b/src/common/SocketWriter.h
index 9b4036f..3c7a977 100644
--- a/src/common/SocketWriter.h
+++ b/src/common/SocketWriter.h
@@ -25,6 +25,7 @@
 #include <QLocalSocket>
 
 #include "Session.h"
+#include "AuthBase.h" // for Request
 
 namespace SDDM {
     class SocketWriter {
@@ -36,6 +37,7 @@ namespace SDDM {
         SocketWriter &operator << (const quint32 &u);
         SocketWriter &operator << (const QString &s);
         SocketWriter &operator << (const Session &s);
+        SocketWriter &operator << (const Request &r);
 
     private:
         QByteArray data;
diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp
index ab84329..5066dd2 100644
--- a/src/daemon/Display.cpp
+++ b/src/daemon/Display.cpp
@@ -56,6 +56,8 @@ namespace SDDM {
         m_socketServer(new SocketServer(this)),
         m_greeter(new Greeter(this)) {
 
+        connect(m_greeter, SIGNAL(stopped()), this, SLOT(slotGreeterStopped()));
+
         // respond to authentication requests
         m_auth->setVerbose(true);
         connect(m_auth, &Auth::requestChanged, this, &Display::slotRequestChanged);
@@ -72,10 +74,17 @@ namespace SDDM {
 
         // connect login signal
         connect(m_socketServer, &SocketServer::login, this, &Display::login);
+        // pam responses and conversation cancel
+        connect(m_socketServer, SIGNAL(sendPamResponse(const QString&)), this, SLOT(setPamResponse(const QString&)));
+        connect(m_socketServer, SIGNAL(cancelPamConv()), this, SLOT(cancelPamConv()));
 
         // connect login result signals
-        connect(this, SIGNAL(loginFailed(QLocalSocket*)), m_socketServer, SLOT(loginFailed(QLocalSocket*)));
+        connect(this, SIGNAL(loginFailed(QLocalSocket*,const QString&)), m_socketServer, SLOT(loginFailed(QLocalSocket*,const QString&)));
         connect(this, SIGNAL(loginSucceeded(QLocalSocket*)), m_socketServer, SLOT(loginSucceeded(QLocalSocket*)));
+        // pam (conversation) info/error shown in greeter
+        connect(this, SIGNAL(pamConvMsg(QLocalSocket*,const QString&)), m_socketServer, SLOT(pamConvMsg(QLocalSocket*,const QString&)));
+        // new request from pam for e.g. password renewal (expired password)
+        connect(this, SIGNAL(pamRequest(QLocalSocket*,const AuthRequest * const)), m_socketServer, SLOT(pamRequest(QLocalSocket*,const AuthRequest * const)));
     }
 
     Display::~Display() {
@@ -221,7 +230,9 @@ namespace SDDM {
     void Display::login(QLocalSocket *socket,
                         const QString &user, const QString &password,
                         const Session &session) {
+
         m_socket = socket;
+        m_failed = false;
 
         //the SDDM user has special privileges that skip password checking so that we can load the greeter
         //block ever trying to log in as the SDDM user
@@ -233,6 +244,20 @@ namespace SDDM {
         startAuth(user, password, session);
     }
 
+    // password renewal, got response from greeter
+    void Display::setPamResponse(const QString &password) {
+        qDebug() << "Display: set pam response with new password";
+        m_auth->request()->setChangeResponse(m_passPhrase, password);
+        if(m_auth->request()->finishAutomatically() ==  false)
+            m_auth->request()->done();
+    }
+
+    // cancel pam (password renewal) conversation
+    // because user canceled password dialog in greeter
+    void Display::cancelPamConv() {
+        m_auth->request()->cancel();
+    }
+
     QString Display::findGreeterTheme() const {
         QString themeName = mainConfig.Theme.Current.get();
 
@@ -351,7 +376,7 @@ namespace SDDM {
 
     void Display::slotAuthenticationFinished(const QString &user, bool success) {
         if (success) {
-            qDebug() << "Authenticated successfully";
+            qDebug() << "Authenticated successfully" << user;
 
             m_auth->setCookie(qobject_cast<XorgDisplayServer *>(m_displayServer)->cookie());
 
@@ -372,28 +397,54 @@ namespace SDDM {
 
             if (m_socket)
                 emit loginSucceeded(m_socket);
-        } else if (m_socket) {
+        } else {
             qDebug() << "Authentication failure";
-            emit loginFailed(m_socket);
+
+            if (!m_socket) {
+                qWarning() << "Greeter socket down!";
+                return;
+            }
+
+            // avoid to emit loginFailed twice
+            if(!m_failed) {
+                emit loginFailed(m_socket, QString());
+                m_failed = true;
+            }
         }
-        m_socket = nullptr;
     }
 
+    // presentable to the user
     void Display::slotAuthInfo(const QString &message, Auth::Info info) {
-        // TODO: presentable to the user, eventually
-        Q_UNUSED(info);
         qWarning() << "Authentication information:" << message;
+
+        if (!m_socket) {
+            qWarning() << "Greeter socket down!";
+            return;
+        }
+
+        if(info == Auth::INFO_PASS_CHANGE_REQUIRED ||
+           info == Auth::INFO_PAM_CONV)
+        {
+            // send pam conversation message to greeter
+            emit pamConvMsg(m_socket, message);
+        }
     }
 
     void Display::slotAuthError(const QString &message, Auth::Error error) {
         // TODO: handle more errors
         qWarning() << "Authentication error:" << message;
 
-        if (!m_socket)
+        if (!m_socket) {
+            qWarning() << "Greeter socket down!";
             return;
+        }
 
-        if (error == Auth::ERROR_AUTHENTICATION)
-            emit loginFailed(m_socket);
+        // avoid to emit loginFailed twice
+        if (error == Auth::ERROR_AUTHENTICATION && !m_failed)
+        {
+            m_failed = true;
+            emit loginFailed(m_socket, message);
+        }
     }
 
     void Display::slotHelperFinished(Auth::HelperExitStatus status) {
@@ -406,18 +457,62 @@ namespace SDDM {
             stop();
     }
 
+    /* got new request (with prompts) from pam conv(), e.g. for expired pwd
+     * which requires response from greeter UI (new password) */
     void Display::slotRequestChanged() {
-        if (m_auth->request()->prompts().length() == 1) {
+
+        int n_prompts = m_auth->request()->prompts().count();
+        AuthPrompt::Type type_prompt0(AuthPrompt::NONE);
+
+        // get type if we have a prompt
+        if(n_prompts>0)
+            type_prompt0 = m_auth->request()->prompts()[0]->type();
+        // ignore empty requests
+        else return;
+
+        // see what we got from pam conv() and will be send to greeter
+        qDebug() << "Display: requestChanged with " << n_prompts << " prompts from Auth";
+
+        // respond to login password request
+        if (n_prompts == 1 && type_prompt0 == AuthPrompt::LOGIN_PASSWORD) {
             m_auth->request()->prompts()[0]->setResponse(qPrintable(m_passPhrase));
             m_auth->request()->done();
-        } else if (m_auth->request()->prompts().length() == 2) {
+            return;
+        }
+
+        // handle request with user name and password prompts
+        if (n_prompts == 2 && type_prompt0 == AuthPrompt::LOGIN_USER )
+        {
             m_auth->request()->prompts()[0]->setResponse(qPrintable(m_auth->user()));
             m_auth->request()->prompts()[1]->setResponse(qPrintable(m_passPhrase));
             m_auth->request()->done();
+            return;
+        }
+
+        // handle password renewal case (gets response from greeter),
+        // will finish with request->done() later in setPamResponse()
+        if (n_prompts >= 2 &&
+            m_auth->request()->findPrompt(AuthPrompt::CHANGE_NEW) &&
+            m_auth->request()->findPrompt(AuthPrompt::CHANGE_REPEAT))
+        {
+            if(m_socket)
+                // send password renewal request to greeter (via SocketServer)
+                emit pamRequest(m_socket, m_auth->request());
+
+            return;
         }
+
+        qWarning() << "Display: Unable to handle Auth request!";
     }
 
     void Display::slotSessionStarted(bool success) {
         qDebug() << "Session started";
     }
+
+    // inform daemon if greeter stopped, so we dont forward
+    // errors or infos etc. through invalid socket
+    void Display::slotGreeterStopped() {
+        qDebug() << "Display: Greeter was stopped";
+        m_socket = nullptr;
+    }
 }
diff --git a/src/daemon/Display.h b/src/daemon/Display.h
index 09d3cf9..a05d2ab 100644
--- a/src/daemon/Display.h
+++ b/src/daemon/Display.h
@@ -60,14 +60,18 @@ namespace SDDM {
         void login(QLocalSocket *socket,
                    const QString &user, const QString &password,
                    const Session &session);
+        void setPamResponse(const QString &password);
+        void cancelPamConv();
         bool attemptAutologin();
         void displayServerStarted();
 
     signals:
         void stopped();
 
-        void loginFailed(QLocalSocket *socket);
+        void loginFailed(QLocalSocket *socket, const QString &message);
         void loginSucceeded(QLocalSocket *socket);
+        void pamConvMsg(QLocalSocket *socket, const QString &message);
+        void pamRequest(QLocalSocket *socket, const AuthRequest * const request);
 
     private:
         QString findGreeterTheme() const;
@@ -78,6 +82,7 @@ namespace SDDM {
 
         bool m_relogin { true };
         bool m_started { false };
+        bool m_failed { false };
 
         int m_terminalId { 7 };
 
@@ -100,6 +105,7 @@ namespace SDDM {
         void slotHelperFinished(Auth::HelperExitStatus status);
         void slotAuthInfo(const QString &message, Auth::Info info);
         void slotAuthError(const QString &message, Auth::Error error);
+        void slotGreeterStopped();
     };
 }
 
diff --git a/src/daemon/Greeter.cpp b/src/daemon/Greeter.cpp
index 0240085..064703c 100644
--- a/src/daemon/Greeter.cpp
+++ b/src/daemon/Greeter.cpp
@@ -223,6 +223,8 @@ namespace SDDM {
             if (!m_process->waitForFinished(5000))
                 m_process->kill();
         }
+
+        emit stopped();
     }
 
     void Greeter::finished() {
@@ -239,6 +241,8 @@ namespace SDDM {
         // clean up
         m_process->deleteLater();
         m_process = nullptr;
+
+        emit stopped();
     }
 
     void Greeter::onRequestChanged() {
@@ -266,6 +270,8 @@ namespace SDDM {
         // clean up
         m_auth->deleteLater();
         m_auth = nullptr;
+
+        emit stopped();
     }
 
     void Greeter::onReadyReadStandardError()
diff --git a/src/daemon/Greeter.h b/src/daemon/Greeter.h
index 7391a35..6fcf613 100644
--- a/src/daemon/Greeter.h
+++ b/src/daemon/Greeter.h
@@ -43,6 +43,9 @@ namespace SDDM {
         void setSocket(const QString &socket);
         void setTheme(const QString &theme);
 
+    signals:
+        void stopped();
+
     public slots:
         bool start();
         void stop();
diff --git a/src/daemon/SocketServer.cpp b/src/daemon/SocketServer.cpp
index 40d72cc..4df2612 100644
--- a/src/daemon/SocketServer.cpp
+++ b/src/daemon/SocketServer.cpp
@@ -26,6 +26,8 @@
 #include "SocketWriter.h"
 #include "Utils.h"
 
+#include "AuthBase.h"
+
 #include <QLocalServer>
 
 namespace SDDM {
@@ -99,9 +101,13 @@ namespace SDDM {
         connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater);
     }
 
+    // from greeter (to backend)
+
     void SocketServer::readyRead() {
         QLocalSocket *socket = qobject_cast<QLocalSocket *>(sender());
 
+        static const char *logPrefix = "SocketServer: Message received from greeter:";
+
         // check socket
         if (!socket)
             return;
@@ -116,7 +122,7 @@ namespace SDDM {
         switch (GreeterMessages(message)) {
             case GreeterMessages::Connect: {
                 // log message
-                qDebug() << "Message received from greeter: Connect";
+                qDebug() << logPrefix << "Connect";
 
                 // send capabilities
                 SocketWriter(socket) << quint32(DaemonMessages::Capabilities) << quint32(daemonApp->powerManager()->capabilities());
@@ -130,7 +136,7 @@ namespace SDDM {
             break;
             case GreeterMessages::Login: {
                 // log message
-                qDebug() << "Message received from greeter: Login";
+                qDebug() << logPrefix << "Login";
 
                 // read username, pasword etc.
                 QString user, password, filename;
@@ -141,58 +147,85 @@ namespace SDDM {
                 emit login(socket, user, password, session);
             }
             break;
+            case GreeterMessages::PamResponse: {
+                qDebug() << logPrefix << "PamResponse";
+
+                // new password from greeter dialog
+                QString newPassword;
+                input >> newPassword;
+
+                // emit signal
+                emit sendPamResponse(newPassword);
+            }
+            break;
+            case GreeterMessages::PamCancel: {
+                qDebug() << logPrefix << "PamCancel";
+
+                // emit signal
+                emit cancelPamConv();
+            }
+            break;
             case GreeterMessages::PowerOff: {
                 // log message
-                qDebug() << "Message received from greeter: PowerOff";
+                qDebug() << logPrefix << "PowerOff";
 
                 // power off
                 daemonApp->powerManager()->powerOff();
             }
             break;
             case GreeterMessages::Reboot: {
-                // log message
-                qDebug() << "Message received from greeter: Reboot";
+                qDebug() << logPrefix << "Reboot";
 
                 // reboot
                 daemonApp->powerManager()->reboot();
             }
             break;
             case GreeterMessages::Suspend: {
-                // log message
-                qDebug() << "Message received from greeter: Suspend";
+                qDebug() << logPrefix << "Suspend";
 
                 // suspend
                 daemonApp->powerManager()->suspend();
             }
             break;
             case GreeterMessages::Hibernate: {
-                // log message
-                qDebug() << "Message received from greeter: Hibernate";
+                qDebug() << logPrefix << "Hibernate";
 
                 // hibernate
                 daemonApp->powerManager()->hibernate();
             }
             break;
             case GreeterMessages::HybridSleep: {
-                // log message
-                qDebug() << "Message received from greeter: HybridSleep";
+                qDebug() << logPrefix << "HybridSleep";
 
                 // hybrid sleep
                 daemonApp->powerManager()->hybridSleep();
             }
             break;
             default: {
-                // log message
-                qWarning() << "Unknown message" << message;
+                qWarning() << logPrefix << "Unknown message" << message;
             }
         }
     }
 
-    void SocketServer::loginFailed(QLocalSocket *socket) {
-        SocketWriter(socket) << quint32(DaemonMessages::LoginFailed);
+    // from (pam) backend to greeter
+
+    void SocketServer::loginFailed(QLocalSocket *socket, const QString &message) {
+        SocketWriter(socket) << quint32(DaemonMessages::LoginFailed) << message;
     }
 
     void SocketServer::loginSucceeded(QLocalSocket *socket) {
         SocketWriter(socket) << quint32(DaemonMessages::LoginSucceeded);
     }
+
+    void SocketServer::pamConvMsg(QLocalSocket *socket, const QString &message) {
+        // send pam converse message to greeter
+        SocketWriter(socket) << quint32(DaemonMessages::PamConvMsg) << message;
+    }
+
+    void SocketServer::pamRequest(QLocalSocket *socket, const AuthRequest * const pam_request) {
+        // convert AuthRequest to simple Request
+        Request request = pam_request->request();
+        // send pam request to greeter
+        SocketWriter(socket) << quint32(DaemonMessages::PamRequest) << request;
+    }
 }
diff --git a/src/daemon/SocketServer.h b/src/daemon/SocketServer.h
index 0d12acb..d3cd85b 100644
--- a/src/daemon/SocketServer.h
+++ b/src/daemon/SocketServer.h
@@ -25,6 +25,7 @@
 #include <QString>
 
 #include "Session.h"
+#include "AuthRequest.h"
 
 class QLocalServer;
 class QLocalSocket;
@@ -45,14 +46,20 @@ namespace SDDM {
         void newConnection();
         void readyRead();
 
-        void loginFailed(QLocalSocket *socket);
+        // from (pam) backend to greeter
+        void loginFailed(QLocalSocket *socket, const QString &message);
         void loginSucceeded(QLocalSocket *socket);
+        void pamConvMsg(QLocalSocket *socket, const QString &message);
+        void pamRequest(QLocalSocket *socket, const AuthRequest * const request);
 
     signals:
+        // from greeter to (pam) backend
         void login(QLocalSocket *socket,
                    const QString &user, const QString &password,
                    const Session &session);
         void connected();
+        void sendPamResponse(const QString &newPassword);
+        void cancelPamConv();
 
     private:
         QLocalServer *m_server { nullptr };
diff --git a/src/greeter/CMakeLists.txt b/src/greeter/CMakeLists.txt
index 12fc0b5..083dca0 100644
--- a/src/greeter/CMakeLists.txt
+++ b/src/greeter/CMakeLists.txt
@@ -1,6 +1,7 @@
 include_directories(
     "${CMAKE_SOURCE_DIR}/src/common"
     "${CMAKE_BINARY_DIR}/src/common"
+    "${CMAKE_SOURCE_DIR}/src/auth"
     "${LIBXCB_INCLUDE_DIR}"
 )
 
@@ -11,6 +12,8 @@ set(GREETER_SOURCES
     ${CMAKE_SOURCE_DIR}/src/common/SocketWriter.cpp
     ${CMAKE_SOURCE_DIR}/src/common/ThemeConfig.cpp
     ${CMAKE_SOURCE_DIR}/src/common/ThemeMetadata.cpp
+    ${CMAKE_SOURCE_DIR}/src/auth/AuthPrompt.cpp
+    ${CMAKE_SOURCE_DIR}/src/auth/AuthRequest.cpp
     GreeterApp.cpp
     GreeterProxy.cpp
     KeyboardLayout.cpp
diff --git a/src/greeter/GreeterApp.cpp b/src/greeter/GreeterApp.cpp
index 5fb70ea..91bb057 100644
--- a/src/greeter/GreeterApp.cpp
+++ b/src/greeter/GreeterApp.cpp
@@ -28,6 +28,8 @@
 #include "ThemeMetadata.h"
 #include "UserModel.h"
 #include "KeyboardModel.h"
+#include "AuthRequest.h"
+#include "AuthPrompt.h"
 
 #include "MessageHandler.h"
 
@@ -180,10 +182,15 @@ namespace SDDM {
         view->rootContext()->setContextProperty(QStringLiteral("userModel"), m_userModel);
         view->rootContext()->setContextProperty(QStringLiteral("config"), *m_themeConfig);
         view->rootContext()->setContextProperty(QStringLiteral("sddm"), m_proxy);
+        view->rootContext()->setContextProperty(QStringLiteral("request"), m_proxy->getRequest());
         view->rootContext()->setContextProperty(QStringLiteral("keyboard"), m_keyboard);
         view->rootContext()->setContextProperty(QStringLiteral("primaryScreen"), QGuiApplication::primaryScreen() == screen);
         view->rootContext()->setContextProperty(QStringLiteral("__sddm_errors"), QString());
 
+        // register AuthRequest and AuthPrompt type for pamRequest signal
+        qmlRegisterUncreatableType<AuthRequest>("SddmAuth", 1, 0, "AuthRequest", QStringLiteral("AuthRequest object creation not allowed"));
+        qmlRegisterUncreatableType<AuthPrompt>("SddmAuth", 1, 0, "AuthPrompt", QStringLiteral("AuthPrompt object creation not allowed"));
+
         // get theme main script
         QString mainScript = QStringLiteral("%1/%2").arg(m_themePath).arg(m_metadata->mainScript());
         QUrl mainScriptUrl;
diff --git a/src/greeter/GreeterProxy.cpp b/src/greeter/GreeterProxy.cpp
index 8759e74..e6991a2 100644
--- a/src/greeter/GreeterProxy.cpp
+++ b/src/greeter/GreeterProxy.cpp
@@ -24,6 +24,8 @@
 #include "Messages.h"
 #include "SessionModel.h"
 #include "SocketWriter.h"
+#include "AuthBase.h"
+#include "AuthRequest.h"
 
 #include <QLocalSocket>
 
@@ -32,6 +34,7 @@ namespace SDDM {
     public:
         SessionModel *sessionModel { nullptr };
         QLocalSocket *socket { nullptr };
+        AuthRequest *request { nullptr };
         QString hostName;
         bool canPowerOff { false };
         bool canReboot { false };
@@ -47,7 +50,8 @@ namespace SDDM {
         connect(d->socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected);
         connect(d->socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead);
         connect(d->socket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this, &GreeterProxy::error);
-
+        // AuthRequest for exchange of PAM conversation prompts/responses with qml
+        d->request = new AuthRequest(this);
         // connect to server
         d->socket->connectToServer(socket);
     }
@@ -64,6 +68,17 @@ namespace SDDM {
         d->sessionModel = model;
     }
 
+    /** AuthRequest for exchange of PAM prompts/responses
+     *  between GreeterProxy and greeter GUI
+     */
+    AuthRequest *GreeterProxy::getRequest() {
+        return d->request;
+    }
+
+    void GreeterProxy::setRequest(Request *r) {
+        d->request->setRequest(r);
+    }
+
     bool GreeterProxy::canPowerOff() const {
         return d->canPowerOff;
     }
@@ -112,8 +127,6 @@ namespace SDDM {
         if (!d->sessionModel) {
             // log error
             qCritical() << "Session model is not set.";
-
-            // return
             return;
         }
 
@@ -127,6 +140,18 @@ namespace SDDM {
         SocketWriter(d->socket) << quint32(GreeterMessages::Login) << user << password << session;
     }
 
+    // todo: mind session index like for login
+    void GreeterProxy::pamResponse(const QString &newPassword) {
+        // send new (renewal) password to daemon for pam conv
+        SocketWriter(d->socket) << quint32(GreeterMessages::PamResponse) << newPassword;
+    }
+
+    // todo: mind session index like for login
+    void GreeterProxy::cancelPamConv() {
+        // send command to daemon (to pam conv() in backend)
+        SocketWriter(d->socket) << quint32(GreeterMessages::PamCancel);
+    }
+
     void GreeterProxy::connected() {
         // log connection
         qDebug() << "Connected to the daemon.";
@@ -197,11 +222,44 @@ namespace SDDM {
                 }
                 break;
                 case DaemonMessages::LoginFailed: {
+                    QString m;
+                    // read pam conv() message (info/error) from daemon
+                    input >> m;
                     // log message
-                    qDebug() << "Message received from daemon: LoginFailed";
-
+                    qDebug() << "Message received from daemon: LoginFailed, " << m;
                     // emit signal
-                    emit loginFailed();
+                    emit loginFailed(m);
+                }
+                break;
+                // pam messages from conv() following login, e.g. for expired pwd
+                // conv() msg with msg_style: PAM_ERROR_MSG or PAM_TEXT_INFO
+                case DaemonMessages::PamConvMsg: {
+                    QString m;
+                    // read pam conv() message (info/error) from daemon
+                    input >> m;
+                    qDebug() << "PAM conversation message from daemon: " << m;
+                    // send message to GUI
+                    emit pamConvMsg(m);
+                }
+                break;
+
+                // new request with prompts from conv() after login, e.g. due to expired pwd
+                // request contains prompt (message) from conv() with msg_style:
+                // PAM_PROMPT_ECHO_ON (user name) or PAM_PROMPT_ECHO_OFF (passwords)
+                case DaemonMessages::PamRequest: {
+                    Request r;
+                    // read request from daemon (Display->SocketServer)
+                    input >> r;
+                    // set pam request  for qml and convert Request to AuthRequest
+                    setRequest(&r);
+                    // log prompts
+                    qDebug() << "PAM request with " << r.prompts.length() << " prompts from daemon";
+                    for (const Prompt &p : r.prompts)
+                        qDebug() << "GreeterProxy: Prompt message=" << p.message << ", hidden=" << p.hidden
+                                        << ", type =" << AuthPrompt::typeToString(p.type) << "(" << p.type << ")";
+
+                    // send pam msg's to GUI
+                    emit pamRequest();
                 }
                 break;
                 default: {
diff --git a/src/greeter/GreeterProxy.h b/src/greeter/GreeterProxy.h
index ba2533f..76f09c9 100644
--- a/src/greeter/GreeterProxy.h
+++ b/src/greeter/GreeterProxy.h
@@ -25,8 +25,9 @@
 class QLocalSocket;
 
 namespace SDDM {
+    class Request;
+    class AuthRequest;
     class SessionModel;
-
     class GreeterProxyPrivate;
     class GreeterProxy : public QObject {
         Q_OBJECT
@@ -54,6 +55,7 @@ namespace SDDM {
         bool isConnected() const;
 
         void setSessionModel(SessionModel *model);
+        AuthRequest *getRequest();
 
     public slots:
         void powerOff();
@@ -62,7 +64,10 @@ namespace SDDM {
         void hibernate();
         void hybridSleep();
 
+        // toward (pam) backend
         void login(const QString &user, const QString &password, const int sessionIndex) const;
+        void pamResponse(const QString &newPassword);
+        void cancelPamConv();
 
     private slots:
         void connected();
@@ -78,11 +83,15 @@ namespace SDDM {
         void canHibernateChanged(bool canHibernate);
         void canHybridSleepChanged(bool canHybridSleep);
 
-        void loginFailed();
+        // toward qml gui
+        void loginFailed(const QString err_msg);
         void loginSucceeded();
+        void pamConvMsg(const QString pam_msg);
+        void pamRequest();
 
     private:
         GreeterProxyPrivate *d { nullptr };
+        void setRequest(Request *r);
     };
 }
 
diff --git a/src/greeter/theme/Main.qml b/src/greeter/theme/Main.qml
index 5052688..6543bd5 100644
--- a/src/greeter/theme/Main.qml
+++ b/src/greeter/theme/Main.qml
@@ -38,15 +38,23 @@ Rectangle {
 
     TextConstants { id: textConstants }
 
+    function resetTxtMsg() {
+        txtMessage.text = textConstants.promptSelectUser
+        txtMessage.color = "white"
+    }
+
+    // container for password renewal logic
+    PasswordConnections {
+        renewalDialog: renewal
+        pwdItem: listView.currentItem // use listView.currentItem.password
+        getsBackFocus: listView
+        errMsg: errMessage
+        txtMsg: txtMessage
+    }
+
     Connections {
         target: sddm
-        onLoginSucceeded: {
-        }
-
-        onLoginFailed: {
-            txtMessage.text = textConstants.loginFailed
-            listView.currentItem.password = ""
-        }
+        onLoginFailed: txtMessage.color = "red"
     }
 
     Background {
@@ -117,10 +125,21 @@ Rectangle {
                 color: "#22000000"
                 clip: true
 
+                PasswordRenewal {
+                    id: renewal
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    anchors.bottom: usersContainer.top
+                    visible: false
+                    color: "#22888888"
+                    promptColor: "white"
+                    infosColor: "lightcoral"
+                }
+
                 Item {
                     id: usersContainer
                     width: parent.width; height: 300
                     anchors.verticalCenter: parent.verticalCenter
+                    enabled: !renewal.visible
 
                     ImageButton {
                         id: prevUser
@@ -149,7 +168,7 @@ Rectangle {
                         delegate: userDelegate
                         orientation: ListView.Horizontal
                         currentIndex: userModel.lastIndex
-
+                        onCurrentIndexChanged: resetTxtMsg()
                         KeyNavigation.backtab: prevUser; KeyNavigation.tab: nextUser
                     }
 
@@ -188,7 +207,9 @@ Rectangle {
                     font.pixelSize: 20
                     visible: __sddm_errors !== ""
                 }
+
             }
+
         }
 
         Rectangle {
diff --git a/src/helper/HelperApp.cpp b/src/helper/HelperApp.cpp
index d942dcc..47a113b 100644
--- a/src/helper/HelperApp.cpp
+++ b/src/helper/HelperApp.cpp
@@ -164,18 +164,32 @@ namespace SDDM {
         m_socket->waitForBytesWritten();
     }
 
-    Request HelperApp::request(const Request& request) {
+    Request HelperApp::request(const Request& request, bool &cancel) {
         Msg m = Msg::MSG_UNKNOWN;
         Request response;
         SafeDataStream str(m_socket);
         str << Msg::REQUEST << request;
         str.send();
+        m_socket->waitForBytesWritten();
         str.receive();
-        str >> m >> response;
-        if (m != REQUEST) {
-            response = Request();
-            qCritical() << "Received a wrong opcode instead of REQUEST:" << m;
-        }
+        str >> m;
+        switch(m) {
+            // user response from daemon (greeter)
+            case REQUEST:
+                str >> response;
+                qDebug() << "HelperApp: daemon response received";
+                break;
+            // password renewal canceled in greeter
+            case CANCEL:
+                cancel = true;
+                qDebug() << "HelperApp: Message received from daemon: CANCEL";
+                // terminate user session in Auth (QProcess child)
+                m_session->terminate();
+                break;
+            default:
+                response = Request();
+                qCritical() << "HelperApp: Received a wrong opcode instead of REQUEST or CANCEL:" << m;
+        } // switch
         return response;
     }
 
@@ -185,6 +199,7 @@ namespace SDDM {
         SafeDataStream str(m_socket);
         str << Msg::AUTHENTICATED << user;
         str.send();
+        m_socket->waitForBytesWritten();
         if (user.isEmpty())
             return env;
         str.receive();
@@ -202,6 +217,7 @@ namespace SDDM {
         SafeDataStream str(m_socket);
         str << Msg::SESSION_STATUS << success;
         str.send();
+        m_socket->waitForBytesWritten();
         str.receive();
         str >> m;
         if (m != SESSION_STATUS) {
diff --git a/src/helper/HelperApp.h b/src/helper/HelperApp.h
index b83eade..e07185c 100644
--- a/src/helper/HelperApp.h
+++ b/src/helper/HelperApp.h
@@ -18,8 +18,8 @@
  *
  */
 
-#ifndef Auth_H
-#define Auth_H
+#ifndef HELPERAPP_H
+#define HELPERAPP_H
 
 #include <QtCore/QCoreApplication>
 #include <QtCore/QProcessEnvironment>
@@ -43,7 +43,7 @@ namespace SDDM {
         const QString &cookie() const;
 
     public slots:
-        Request request(const Request &request);
+        Request request(const Request &request, bool &cancel);
         void info(const QString &message, Auth::Info type);
         void error(const QString &message, Auth::Error type);
         QProcessEnvironment authenticated(const QString &user);
@@ -52,7 +52,6 @@ namespace SDDM {
     private slots:
         void setUp();
         void doAuth();
-
         void sessionFinished(int status);
 
     private:
@@ -66,4 +65,4 @@ namespace SDDM {
     };
 }
 
-#endif // Auth_H
+#endif // HELPERAPP_H
diff --git a/src/helper/backend/PamBackend.cpp b/src/helper/backend/PamBackend.cpp
index 69cbd2c..4d1a1d0 100644
--- a/src/helper/backend/PamBackend.cpp
+++ b/src/helper/backend/PamBackend.cpp
@@ -106,11 +106,14 @@ namespace SDDM {
 
     /*
     * Expects an empty prompt list if the previous request has been processed
+    *
+    * @return true if new prompt was inserted or prompt message was set
+    * and false if prompt was found and already sent
     */
     bool PamData::insertPrompt(const struct pam_message* msg, bool predict) {
         Prompt &p = findPrompt(msg);
 
-        // first, check if we already have stored this propmpt
+        // first, check if we already have stored this prompt
         if (p.valid()) {
             // we have a response already - do nothing
             if (m_sent)
@@ -152,13 +155,54 @@ namespace SDDM {
         return true;
     }
 
-    Auth::Info PamData::handleInfo(const struct pam_message* msg, bool predict) {
-        if (QString::fromLocal8Bit(msg->msg).indexOf(QRegExp(QStringLiteral("^Changing password for [^ ]+$")))) {
-            if (predict)
+    /*
+     * Distinguish types of pam conversation infos.
+     *
+     * Currently different info types are not really used (see \ref Display::slotAuthInfo which
+     * just fires one signal for all info types). Just builds new request for "changing" keyword.
+     */
+    Auth::Info PamData::handleInfo(const struct pam_message* msg, bool &newRequest, bool predict) {
+        if (QString::fromLocal8Bit(msg->msg).indexOf(QRegExp(QStringLiteral("^Changing password for [^ ]+$"))) >= 0)
+        {
+            if (predict) {
                 m_currentRequest = Request(changePassRequest);
+                newRequest = true;
+                m_sent = false;
+            }
             return Auth::INFO_PASS_CHANGE_REQUIRED;
         }
-        return Auth::INFO_UNKNOWN;
+        return Auth::INFO_PAM_CONV;
+    }
+
+    /* Figure out if pam (chAuthTok) sent an error or just an info during conversation
+     *
+     * Real error message e.g. "Password change aborted." But most are just info messages
+     * e.g. "change your password ... (administrator enforced)" with type PAM_ERROR_MSG.
+     */
+    Auth::Error PamData::handleErr(const struct pam_message* msg, bool &newRequest, bool predict) {
+        // bad password during password renewal (for expired password)
+        if (QString::fromLocal8Bit(msg->msg).startsWith(QStringLiteral("BAD PASSWORD:")))
+        {
+            if (predict)
+            {
+                // prepare new (empty) request, new password prompt will follow
+                m_currentRequest = Request();
+                newRequest = false;
+                m_sent = false;
+            }
+            return Auth::ERROR_NONE;
+        }
+
+        if(QString::fromLocal8Bit(msg->msg).startsWith(QStringLiteral("Password change aborted."))) {
+            return Auth::ERROR_AUTHENTICATION;
+        }
+
+        // first login for user, home dir will be created, ignore error message (which would shutdown sddm)
+        if(QString::fromLocal8Bit(msg->msg).startsWith(QStringLiteral("Unable to create and initialize directory"))) {
+            return Auth::ERROR_NONE;
+        }
+
+        return Auth::ERROR_NONE;
     }
 
     /*
@@ -172,6 +216,7 @@ namespace SDDM {
         return response;
     }
 
+    /* As long as m_currentRequest is not sent (m_sent is false) return current request */
     const Request& PamData::getRequest() const {
         if (!m_sent)
             return m_currentRequest;
@@ -179,6 +224,7 @@ namespace SDDM {
             return invalidRequest;
     }
 
+    /* Use new request if prompts are equal to current one and set sent true. */
     void PamData::completeRequest(const Request& request) {
         if (request.prompts.length() != m_currentRequest.prompts.length()) {
             qWarning() << "[PAM] Different request/response list length, ignoring";
@@ -232,6 +278,7 @@ namespace SDDM {
     }
 
     bool PamBackend::authenticate() {
+        m_convCanceled = false; // reset for converse()
         if (!m_pam->authenticate()) {
             m_app->error(m_pam->errorString(), Auth::ERROR_AUTHENTICATION);
             return false;
@@ -291,6 +338,32 @@ namespace SDDM {
         return QString::fromLocal8Bit((const char*) m_pam->getItem(PAM_USER));
     }
 
+    /** @internal for debug log: get string representation of pam message msg_style */
+    const QString &PamBackend::msgStyleString(int msg_style)
+    {
+        static const QString msgStyle[] = {
+            QStringLiteral("PAM_PROMPT_ECHO_OFF"),
+            QStringLiteral("PAM_PROMPT_ECHO_ON"),
+            QStringLiteral("PAM_ERROR_MSG"),
+            QStringLiteral("PAM_TEXT_INFO"),
+            QStringLiteral("UNKNOWN"),
+        };
+
+        switch(msg_style) {
+            case PAM_PROMPT_ECHO_OFF:
+                return msgStyle[0]; break;
+            case PAM_PROMPT_ECHO_ON:
+                return msgStyle[1]; break;
+            case PAM_ERROR_MSG:
+                return msgStyle[2]; break;
+            case PAM_TEXT_INFO:
+                return msgStyle[3]; break;
+            default: break;
+        }
+
+        return msgStyle[4];
+    }
+
     int PamBackend::converse(int n, const struct pam_message **msg, struct pam_response **resp) {
         qDebug() << "[PAM] Conversation with" << n << "messages";
 
@@ -299,34 +372,69 @@ namespace SDDM {
         if (n <= 0 || n > PAM_MAX_NUM_MSG)
             return PAM_CONV_ERR;
 
+        // see whats going on in pam_conv
         for (int i = 0; i < n; i++) {
+            qDebug() << "[PAM] pam_conv: style=" << msgStyleString(msg[i]->msg_style)
+                            << "(" << msg[i]->msg_style << "), msg[" << i << "]=" << QString::fromLocal8Bit(msg[i]->msg);
+        }
+
+        for (int i = 0; i < n; i++) {
+
+            QString convMsg = QString::fromLocal8Bit(msg[i]->msg);
+            Auth::Error convErr;
+
             switch(msg[i]->msg_style) {
                 case PAM_PROMPT_ECHO_OFF:
                 case PAM_PROMPT_ECHO_ON:
                     newRequest = m_data->insertPrompt(msg[i], n == 1);
                     break;
                 case PAM_ERROR_MSG:
-                    m_app->error(QString::fromLocal8Bit(msg[i]->msg), Auth::ERROR_AUTHENTICATION);
+                    convErr = m_data->handleErr(msg[i], newRequest, n == 1);
+                    // upon pwd change pam sends error message and then starts conversation,
+                    // so treat most of them actually as infos
+                    if(convErr==Auth::ERROR_NONE)
+                        m_app->info(convMsg, Auth::INFO_PAM_CONV);
+                    else {
+                        qDebug() << "[PAM] PamBackend: pam error message, msg=" << convMsg;
+                        m_app->error(convMsg, Auth::ERROR_AUTHENTICATION /* convErr */);
+                        return PAM_CONV_ERR;
+                    }
                     break;
                 case PAM_TEXT_INFO:
+                    // send pam conversation msg to greeter via HelperApp, SocketServer, Display
                     // if there's only the info message, let's predict the prompts too
-                    m_app->info(QString::fromLocal8Bit(msg[i]->msg), m_data->handleInfo(msg[i], n == 1));
+                    m_app->info(convMsg, m_data->handleInfo(msg[i], newRequest, n == 1));
                     break;
                 default:
                     break;
             }
         }
 
+        // pam (chauthtok) does not shut up after canceled with PAM_CONV_ERR in previous pam_conv
+        if(m_convCanceled) {
+            qDebug() << "[PAM] PamBackend: conversation canceled, dump trailing messages";
+            return PAM_CONV_ERR;
+        }
+
         if (newRequest) {
-            Request sent = m_data->getRequest();
+            // get current request
+            Request send = m_data->getRequest();
             Request received;
 
-            if (sent.valid()) {
-                received = m_app->request(sent);
+            // any prompt?
+            if (send.valid()) {
+                // send request to daemon (ask for a response)
+                received = m_app->request(send, m_convCanceled);
+
+                // password input canceled in greeter
+                if (m_convCanceled) {
+                    return PAM_CONV_ERR;
+                }
 
                 if (!received.valid())
                     return PAM_CONV_ERR;
 
+                // compare request with response
                 m_data->completeRequest(received);
             }
         }
diff --git a/src/helper/backend/PamBackend.h b/src/helper/backend/PamBackend.h
index 4c8b4b3..8784f21 100644
--- a/src/helper/backend/PamBackend.h
+++ b/src/helper/backend/PamBackend.h
@@ -37,7 +37,8 @@ namespace SDDM {
         PamData();
 
         bool insertPrompt(const struct pam_message *msg, bool predict = true);
-        Auth::Info handleInfo(const struct pam_message *msg, bool predict);
+        Auth::Info handleInfo(const struct pam_message *msg, bool &newRequest, bool predict);
+        Auth::Error handleErr(const struct pam_message *msg, bool &newRequest, bool predict);
 
         const Request& getRequest() const;
         void completeRequest(const Request& request);
@@ -71,8 +72,10 @@ namespace SDDM {
         virtual QString userName();
 
     private:
+        bool m_convCanceled { false };
         PamData *m_data { nullptr };
         PamHandle *m_pam { nullptr };
+        static const QString &msgStyleString(int msg_style);
     };
 }
 
diff --git a/src/helper/backend/PasswdBackend.cpp b/src/helper/backend/PasswdBackend.cpp
index 8a0b8c9..27e7435 100644
--- a/src/helper/backend/PasswdBackend.cpp
+++ b/src/helper/backend/PasswdBackend.cpp
@@ -55,7 +55,9 @@ namespace SDDM {
             r.prompts << Prompt(AuthPrompt::LOGIN_USER, QStringLiteral("Login"), false);
         r.prompts << Prompt(AuthPrompt::LOGIN_PASSWORD, QStringLiteral("Password"), true);
 
-        Request response = m_app->request(r);
+        bool canceled = false;
+        Request response = m_app->request(r,canceled);
+        if(canceled==true) return false; // canceled in greeter
         for(const Prompt &p : qAsConst(response.prompts)) {
             switch (p.type) {
                 case AuthPrompt::LOGIN_USER:
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin