2
* Copyright (C) 2014 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Author : Arto Jalkanen <ajalkane@gmail.com>
19
#include "pamauthentication.h"
22
#include <QtDBus/QtDBus>
24
#include <security/pam_appl.h>
26
#define UNITYGREETER_SERVICE "com.canonical.UnityGreeter"
27
#define UNITYGREETER_PATH "/list"
28
#define UNITYGREETER_INTERFACE "com.canonical.UnityGreeter.List"
29
#define UNITYGREETER_PROPERTY_ENTRY_IS_LOCKED "EntryIsLocked"
32
PamAuthentication::PamAuthentication(QObject *parent) :
35
m_userLogin = qgetenv("USER");
38
PamAuthentication::~PamAuthentication() {
43
PamAuthentication::setServiceName(const QString &serviceName) {
44
if (serviceName != m_serviceName) {
45
m_serviceName = serviceName;
46
emit serviceNameChanged();
51
PamAuthentication::requireAuthentication() {
52
// Desktop doesn't have yet Unity8 and so no unity greeter either. Consequently it doesn't
53
// also have any "PIN code" or "Password" extra authentication. Don't require any extra
54
// authentication there.
55
if (qgetenv("QT_QPA_PLATFORM") != "ubuntumirclient") {
56
qDebug() << Q_FUNC_INFO << "Running on non-MIR desktop, not requiring authentication";
60
QDBusInterface dbus_iface(UNITYGREETER_SERVICE, UNITYGREETER_PATH, UNITYGREETER_INTERFACE);
62
qDebug() << Q_FUNC_INFO << "Querying if authentication required";
64
if (!dbus_iface.isValid()) {
65
qDebug() << Q_FUNC_INFO << "Not a valid dbus interface";
66
qDebug() << Q_FUNC_INFO << "Last error: " + dbus_iface.lastError().message();
67
// By default be cautious and require authentication
70
QVariant isLockedVariant = dbus_iface.property(UNITYGREETER_PROPERTY_ENTRY_IS_LOCKED);
71
if (isLockedVariant.isValid()) {
72
bool replyValue = isLockedVariant.toBool();
73
qDebug() << Q_FUNC_INFO << "Return value" << replyValue;
76
qDebug() << Q_FUNC_INFO << "Failed getting value for EntryIsLocked property";
78
// By default be cautious and require authentication
83
PamAuthentication::validatePasswordToken(const QString &token) {
84
pam_handle *pamHandle = 0;
85
if (!initPam(&pamHandle)) {
86
qDebug() << Q_FUNC_INFO << "Pam init failed";
90
m_passwordToken = token;
92
int status = pam_authenticate(pamHandle, 0);
93
qDebug() << Q_FUNC_INFO << "Pam authenticate status" << status << pam_strerror(pamHandle, status);
94
if (status == PAM_SUCCESS) {
95
status = validateAccount(pamHandle);
97
pam_end(pamHandle, status);
99
m_passwordToken.clear();
101
return status == PAM_SUCCESS;
105
PamAuthentication::validateAccount(pam_handle *pamHandle) {
106
// This makes sure account and password are still valid
107
int status = pam_acct_mgmt(pamHandle, 0);
108
qDebug() << Q_FUNC_INFO << "pam_acct_mgmt: " << status << pam_strerror(pamHandle, status);
109
// Placeholders for some common errors
110
// IMPROVE: it'd be good to let user know reason for failure
114
case PAM_USER_UNKNOWN:
116
case PAM_ACCT_EXPIRED:
118
case PAM_NEW_AUTHTOK_REQD:
125
PamAuthentication::initPam(pam_handle **pamHandle)
127
pam_conv conversation;
128
conversation.conv = ConversationFunction;
129
conversation.appdata_ptr = static_cast<void *>(this);
131
return pam_start(m_serviceName.toLocal8Bit().data(), m_userLogin.toLocal8Bit().data(),
132
&conversation, pamHandle) == PAM_SUCCESS;
135
int PamAuthentication::ConversationFunction(int num_msg,
136
const pam_message **msg,
144
*resp = static_cast<pam_response*>(calloc(num_msg, sizeof(pam_response)));
146
PamAuthentication *self = static_cast<PamAuthentication*>(appdata_ptr);
148
for (int count = 0; count < num_msg; ++count) {
149
switch (msg[count]->msg_style) {
150
case PAM_PROMPT_ECHO_ON:
152
qDebug() << Q_FUNC_INFO << "PAM_PROMPT_ECHO_ON received";
153
resp[count]->resp = strdup(self->m_passwordToken.toLocal8Bit().data());
154
resp[count]->resp_retcode = 0;
157
case PAM_PROMPT_ECHO_OFF:
159
qDebug() << Q_FUNC_INFO << "PAM_PROMPT_ECHO_OFF received";
160
resp[count]->resp = strdup(self->m_passwordToken.toLocal8Bit().data());
161
resp[count]->resp_retcode = 0;
166
QString message(msg[count]->msg);
167
qDebug() << Q_FUNC_INFO << "PAM_TEXT_INFO received" << message;
172
qDebug() << Q_FUNC_INFO << "PAM_AUTHTOK received";
177
qDebug() << Q_FUNC_INFO << "Other PAM msg received: " << msg[count]->msg_style;