From 2f48a3c64bf8e096ef4e656685727de580f35db4 Mon Sep 17 00:00:00 2001 From: liuxinhao Date: Mon, 5 Jun 2023 14:57:35 +0800 Subject: [PATCH] feat(auth error): Subdivide the cause of the error and determine whether to record the error according to the cause and mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 细分错误原因,根据原因以及模式不同,判断是否记录错误,例如多路认证不存在认证设备或认证被取消的情况下将不记录内部错误次数以及外部faillock次数 --- ....kylinsec.Kiran.Authentication.Session.xml | 2 + data/kiran-authentication-service | 2 +- src/daemon/session.cpp | 146 +++++++++++------- src/daemon/session.h | 17 +- src/pam/authentication.cpp | 7 + src/pam/authentication.h | 1 + 6 files changed, 118 insertions(+), 57 deletions(-) diff --git a/data/com.kylinsec.Kiran.Authentication.Session.xml b/data/com.kylinsec.Kiran.Authentication.Session.xml index ac737b8..b261dec 100644 --- a/data/com.kylinsec.Kiran.Authentication.Session.xml +++ b/data/com.kylinsec.Kiran.Authentication.Session.xml @@ -80,6 +80,8 @@ + + The authentication type being or to be performed. Refer to enum AuthType in file kas-authentication-i.h. diff --git a/data/kiran-authentication-service b/data/kiran-authentication-service index 8bbbea7..00acda3 100644 --- a/data/kiran-authentication-service +++ b/data/kiran-authentication-service @@ -6,7 +6,7 @@ # =========================认证配置项目================================ # # 多路认证模式,成/功则认证通过,失败/切换到密码 跳过多因子认证模式 -auth [success=done ignore=2 default=bad] pam_kiran_authentication.so doauth +auth [success=done ignore=2 default=bad authinfo_unavail=die] pam_kiran_authentication.so doauth # 多因子认证模式, 成功继续执行PAM流程栈,失败或默认值都为失败 #auth [success=2 default=bad] pam_kiran_authentication.so doauth # ==================================================================== # diff --git a/src/daemon/session.cpp b/src/daemon/session.cpp index 5144da1..b679349 100644 --- a/src/daemon/session.cpp +++ b/src/daemon/session.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace Kiran { @@ -49,29 +50,27 @@ Session::Session(uint32_t sessionID, { this->m_dbusAdaptor = new SessionAdaptor(this); this->m_objectPath = QDBusObjectPath(QString("%1/%2").arg(KAD_SESSION_DBUS_OBJECT_PATH).arg(this->m_sessionID)); - this->m_authMode = AuthManager::getInstance()->getAuthMode(); + this->m_authMode = AuthManager::getInstance()->getAuthMode(); auto authTypes = AuthManager::getInstance()->GetAuthTypeByApp(m_authApplication); this->m_authType = authTypes.count() > 0 ? authTypes.first() : KAD_AUTH_TYPE_NONE; - if (m_authMode == KAD_AUTH_MODE_AND) { this->m_authOrderWaiting = authTypes; - // 多因子认证时,不允许调整用户登录 this->m_verifyInfo.m_authenticatedUserName = m_userName; } - KLOG_DEBUG() << QString("new session authmode(%1),login user switchable(%2),default auth type(%3),auth order(%4)") - .arg(m_authMode) - .arg(m_loginUserSwitchable) - .arg(Utils::authTypeEnum2Str(m_authType)) - .arg(Utils::authOrderEnum2Str(m_authOrderWaiting).join(",")); - auto systemConnection = QDBusConnection::systemBus(); if (!systemConnection.registerObject(this->m_objectPath.path(), this)) { KLOG_WARNING() << m_sessionID << "can't register object:" << systemConnection.lastError(); } + + KLOG_DEBUG() << QString("new session authmode(%1),login user switchable(%2),default auth type(%3),auth order(%4)") + .arg(m_authMode) + .arg(m_loginUserSwitchable) + .arg(Utils::authTypeEnum2Str(m_authType)) + .arg(Utils::authOrderEnum2Str(m_authOrderWaiting).join(",")); } Session::~Session() @@ -206,7 +205,7 @@ void Session::interrupt() void Session::cancel() { KLOG_DEBUG() << m_sessionID << "session (request id:" << this->m_verifyInfo.m_requestID << ") cancel"; - this->finishPhaseAuth(false, false); + this->finishPhaseAuth(SESSION_AUTH_CANCEL); } void Session::end() @@ -244,7 +243,7 @@ void Session::onIdentifyStatus(const QString &bid, int result, const QString &me if (result == IdentifyStatus::IDENTIFY_STATUS_MATCH || result == IdentifyStatus::IDENTIFY_STATUS_NOT_MATCH) { - this->finishPhaseAuth(result == IdentifyStatus::IDENTIFY_STATUS_MATCH, m_authMode == KAD_AUTH_MODE_OR); + this->finishPhaseAuth(result == IDENTIFY_STATUS_MATCH ? SESSION_AUTH_MATCH : SESSION_AUTH_NOT_MATCH); } } @@ -285,14 +284,14 @@ void Session::startUkeyAuth() void Session::startPasswdAuth() { KLOG_DEBUG() << "The authentication service does not take over password authentication,ignore!"; - + this->m_verifyInfo.m_inAuth = true; if (this->m_verifyInfo.m_authenticatedUserName.isEmpty()) { this->m_verifyInfo.m_authenticatedUserName = m_userName; } - - this->finishPhaseAuth(true, false); + + this->finishPhaseAuth(SESSION_AUTH_PASSWD_AUTH_IGNORE); } void Session::startGeneralAuth(const QString &extraInfo) @@ -303,7 +302,7 @@ void Session::startGeneralAuth(const QString &extraInfo) auto authTypeStr = Utils::authTypeEnum2Str(this->m_authType); KLOG_WARNING() << m_sessionID << "start phase auth failed,invalid auth type:" << m_authType; Q_EMIT this->AuthMessage(tr(QString("Auth type %1 invalid").arg(authTypeStr).toStdString().c_str()), KADMessageType::KAD_MESSAGE_TYPE_ERROR); - this->finishPhaseAuth(false, false); + this->finishPhaseAuth(SESSION_AUTH_INTERNAL_ERROR); return; } @@ -313,8 +312,7 @@ void Session::startGeneralAuth(const QString &extraInfo) auto authTypeStr = Utils::authTypeEnum2Str(this->m_authType); KLOG_WARNING() << m_sessionID << "start phase auth failed,can not find device,auth type:" << m_authType; Q_EMIT this->AuthMessage(QString(tr("can not find %1 device")).arg(Utils::authTypeEnum2LocaleStr(this->m_authType)), KADMessageType::KAD_MESSAGE_TYPE_ERROR); - - this->finishPhaseAuth(false, false); + this->finishPhaseAuth(SESSION_AUTH_NO_DEVICE); return; } @@ -344,69 +342,94 @@ void Session::startGeneralAuth(const QString &extraInfo) this->m_verifyInfo.deviceAdaptor->identify(this, doc.toJson(QJsonDocument::Compact)); } -void Session::finishPhaseAuth(bool isSuccess, bool recordFailure) +void Session::finishPhaseAuth(SessionAuthResult authResult) { + auto authResultEnum = QMetaEnum::fromType(); + auto authResultKey = authResultEnum.valueToKey(authResult); + KLOG_DEBUG() << m_sessionID << "session finish phase auth, auth type:" << this->m_authType - << "auth result:" << isSuccess - << "record failure:" << recordFailure; - - // 如果阶段认证失败,则直接结束 - if (!isSuccess) - { - this->finishAuth(isSuccess, recordFailure); - return; - } + << "auth result:" << (authResultKey ? authResultKey : "NULL"); - // 阶段认证成功则进入下个阶段 - switch (this->m_authMode) + switch (authResult) { - case KADAuthMode::KAD_AUTH_MODE_OR: - this->finishAuth(isSuccess, recordFailure); - break; - case KADAuthMode::KAD_AUTH_MODE_AND: + case SESSION_AUTH_MATCH: + case SESSION_AUTH_PASSWD_AUTH_IGNORE: { - if (this->m_authOrderWaiting.size() > 0) + if (this->m_authMode == KAD_AUTH_MODE_OR) { - this->m_authOrderWaiting.removeOne(this->m_authType); - } - - if (this->m_authOrderWaiting.size() == 0) - { - this->finishAuth(isSuccess, recordFailure); + // 多路认证,认证一个通过即算通过 + this->finishAuth(authResult); } else { - this->m_authType = this->m_authOrderWaiting.first(); - this->startPhaseAuth(); + // 检测是否所有认证类型都已通过 + // 存在还未认证,则继续开始认证 + if (this->m_authOrderWaiting.size() > 0) + { + this->m_authOrderWaiting.removeOne(this->m_authType); + } + + if (this->m_authOrderWaiting.size() == 0) + { + this->finishAuth(SESSION_AUTH_MATCH); + } + else + { + this->m_authType = this->m_authOrderWaiting.first(); + this->startPhaseAuth(); + } } break; } + case SESSION_AUTH_NOT_MATCH: + case SESSION_AUTH_NO_DEVICE: + case SESSION_AUTH_CANCEL: + case SESSION_AUTH_INTERNAL_ERROR: + { + // 阶段认证失败,则算失败 + this->finishAuth(authResult); + break; + } default: + KLOG_ERROR() << m_sessionID << "invalid session auth result:" << authResult << (authResultKey ? authResultKey : "NULL"); break; } } -void Session::finishAuth(bool isSuccess, bool recordFailure) +void Session::finishAuth(SessionAuthResult authResult) { - KLOG_DEBUG() << m_sessionID << "finish auth" - << "auth result:" << isSuccess - << "record failure:" << recordFailure; + auto authResultEnum = QMetaEnum::fromType(); + auto authResultKey = authResultEnum.valueToKey(authResult); + KLOG_DEBUG() << m_sessionID << "finish auth\n" + << "auth result:" << (authResultKey ? authResultKey : "NULL"); const QString &authenticatedUserName = this->m_verifyInfo.m_authenticatedUserName; - if (isSuccess && !authenticatedUserName.isEmpty()) + bool isSuccess = (authResult == SESSION_AUTH_MATCH) || (authResult == SESSION_AUTH_PASSWD_AUTH_IGNORE); + if (isSuccess) { - // 认证成功,清空认证通过用户的生物认证错误次数(针对于登录过程中用户跳转) - auto user = UserManager::getInstance()->findUser(authenticatedUserName); - if (user) + if (authenticatedUserName.isEmpty()) { - user->setFailures(0); + KLOG_ERROR() << "authentication succeeded, but the user name was empty!"; + } + else + { + auto user = UserManager::getInstance()->findUser(authenticatedUserName); + if (user) + { + user->setFailures(0); + } + Q_EMIT this->AuthSuccessed(authenticatedUserName); } - Q_EMIT this->AuthSuccessed(authenticatedUserName); } else { - if (recordFailure) + // 是否记录内部错误,内部错误达到上限将不能使用生物认证,只能使用密码解锁 + // 只在多路认证情况下,并且是特征不匹配的情况下记录 + bool recordInternalFailure = (this->m_authMode == KAD_AUTH_MODE_OR) && + (authResult == SESSION_AUTH_NOT_MATCH); + + if (recordInternalFailure) { // 认证失败,未通过一次阶段认证,记录失败用户为发起登录请求的用户 const QString ¤tUser = authenticatedUserName.isEmpty() ? m_userName : authenticatedUserName; @@ -416,8 +439,23 @@ void Session::finishAuth(bool isSuccess, bool recordFailure) user->setFailures(user->getFailures() + 1); } } - Q_EMIT this->AuthFailed(); + + // 是否记录外部failock错误,达到上限,将会锁定账户 + // 多因子认证情况下,任何错误,都将被failock记录 + // 多路认证情况下,只有特征不匹配才被failock记录 + bool recordFailure = (this->m_authMode == KAD_AUTH_MODE_AND) || + (authResult == SESSION_AUTH_NOT_MATCH); + + if (recordFailure) + { + Q_EMIT this->AuthFailed(); + } + else + { + Q_EMIT this->AuthUnavail(); + } } + m_verifyInfo.m_inAuth = false; } diff --git a/src/daemon/session.h b/src/daemon/session.h index a850237..ed97a16 100644 --- a/src/daemon/session.h +++ b/src/daemon/session.h @@ -37,6 +37,18 @@ class Session : public QObject, Q_PROPERTY(uint ID READ getID) Q_PROPERTY(QString RSAPublicKey READ getRSAPublicKey) Q_PROPERTY(QString Username READ getUsername) +public: + enum SessionAuthResult + { + SESSION_AUTH_MATCH, // 特征匹配 + SESSION_AUTH_NOT_MATCH, // 特征不匹配 + SESSION_AUTH_PASSWD_AUTH_IGNORE, // 多因子认证模式,放行密码认证 + SESSION_AUTH_NO_DEVICE, // 不存在该设备 + SESSION_AUTH_CANCEL, // 认证会话被取消 + SESSION_AUTH_INTERNAL_ERROR, // 内部错误 + SESSION_AUTH_LAST + }; + Q_ENUM(SessionAuthResult) public: // 如果只允许对特定用户进行认证,则创建对象时需要指定用户名 Session(uint32_t sessionID, @@ -68,6 +80,7 @@ Q_SIGNALS: // SIGNALS void AuthMessage(const QString &text, int type); void AuthPrompt(const QString &text, int type); void AuthSuccessed(const QString &username); + void AuthUnavail(); private: struct SessionVerifyInfo @@ -101,8 +114,8 @@ private: void startPasswdAuth(); void startGeneralAuth(const QString &extraInfo = QString()); - void finishPhaseAuth(bool isSuccess,bool recordFailure = true); - void finishAuth(bool isSuccess,bool recordFailures = true); + void finishPhaseAuth(SessionAuthResult authResult); + void finishAuth(SessionAuthResult authResult); bool matchUser(int32_t authType, const QString &dataID); diff --git a/src/pam/authentication.cpp b/src/pam/authentication.cpp index 06bc010..0e3b6f0 100644 --- a/src/pam/authentication.cpp +++ b/src/pam/authentication.cpp @@ -271,6 +271,7 @@ bool Authentication::initSession() connect(this->m_authSessionProxy, &AuthSessionProxy::AuthPrompt, this, &Authentication::onAuthPrompt); connect(this->m_authSessionProxy, &AuthSessionProxy::AuthMessage, this, &Authentication::onAuthMessage); connect(this->m_authSessionProxy, &AuthSessionProxy::AuthFailed, this, &Authentication::onAuthFailed); + connect(this->m_authSessionProxy, &AuthSessionProxy::AuthUnavail, this, &Authentication::onAuthUnavail); connect(this->m_authSessionProxy, &AuthSessionProxy::AuthSuccessed, this, &Authentication::onAuthSuccessed); this->m_pamHandle->syslog(LOG_DEBUG, QString("init session,%1").arg(m_sessionID)); return true; @@ -335,6 +336,12 @@ void Authentication::onAuthFailed() this->finishAuth(PAM_AUTH_ERR); } +void Authentication::onAuthUnavail() +{ + this->m_pamHandle->syslog(LOG_DEBUG, QString("Authentication unavail,session ID:%1").arg(m_sessionID)); + this->finishAuth(PAM_AUTHINFO_UNAVAIL); +} + void Authentication::onAuthSuccessed(const QString &userName) { if (!userName.isEmpty()) diff --git a/src/pam/authentication.h b/src/pam/authentication.h index f6cc5a5..704dfc1 100644 --- a/src/pam/authentication.h +++ b/src/pam/authentication.h @@ -64,6 +64,7 @@ private Q_SLOTS: void onAuthPrompt(const QString &text, int type); void onAuthMessage(const QString &text, int type); void onAuthFailed(); + void onAuthUnavail(); void onAuthSuccessed(const QString &userName); void onAuthTypeChanged(int authType); -- Gitee