diff --git a/GPClient/CMakeLists.txt b/GPClient/CMakeLists.txt index 9c52cdd..26109b4 100644 --- a/GPClient/CMakeLists.txt +++ b/GPClient/CMakeLists.txt @@ -33,6 +33,9 @@ add_executable(gpclient gpclient.ui normalloginwindow.ui settingsdialog.ui + challengedialog.h + challengedialog.cpp + challengedialog.ui resources.qrc ${gpclient_GENERATED_SOURCES} ) diff --git a/GPClient/challengedialog.cpp b/GPClient/challengedialog.cpp new file mode 100644 index 0000000..0fd3e80 --- /dev/null +++ b/GPClient/challengedialog.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include "challengedialog.h" +#include "ui_challengedialog.h" + +ChallengeDialog::ChallengeDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ChallengeDialog) +{ + ui->setupUi(this); + ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true); +} + +ChallengeDialog::~ChallengeDialog() +{ + delete ui; +} + +void ChallengeDialog::setMessage(const QString &message) +{ + ui->challengeMessage->setText(message); +} + +const QString ChallengeDialog::getChallenge() +{ + return ui->challengeInput->text(); +} + +void ChallengeDialog::on_challengeInput_textChanged(const QString &value) +{ + QPushButton *okBtn = ui->buttonBox->button(QDialogButtonBox::Ok); + if (value.isEmpty()) { + okBtn->setDisabled(true); + } else { + okBtn->setEnabled(true); + } +} + diff --git a/GPClient/challengedialog.h b/GPClient/challengedialog.h new file mode 100644 index 0000000..bd6b95d --- /dev/null +++ b/GPClient/challengedialog.h @@ -0,0 +1,28 @@ +#ifndef CHALLENGEDIALOG_H +#define CHALLENGEDIALOG_H + +#include + +namespace Ui { +class ChallengeDialog; +} + +class ChallengeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ChallengeDialog(QWidget *parent = nullptr); + ~ChallengeDialog(); + + void setMessage(const QString &message); + const QString getChallenge(); + +private slots: + void on_challengeInput_textChanged(const QString &arg1); + +private: + Ui::ChallengeDialog *ui; +}; + +#endif // CHALLENGEDIALOG_H diff --git a/GPClient/challengedialog.ui b/GPClient/challengedialog.ui new file mode 100644 index 0000000..6aafa22 --- /dev/null +++ b/GPClient/challengedialog.ui @@ -0,0 +1,111 @@ + + + ChallengeDialog + + + + 0 + 0 + 405 + 200 + + + + GlobalProtect Challenge + + + true + + + + + + + + + 14 + 50 + false + + + + Sign In + + + + + + + Duo two-factor login for [redacted] Enter a passcode or select one of the following options: 1. Duo Push to XXX-XXX-[redacted] 2. SMS passcodes to XXX-XXX-[redacted] Passcode or option (1-2): + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + QLineEdit::Password + + + + + + + Qt::LeftToRight + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + buttonBox + accepted() + ChallengeDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ChallengeDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/GPClient/gatewayauthenticator.cpp b/GPClient/gatewayauthenticator.cpp index 60e98de..fbf1014 100644 --- a/GPClient/gatewayauthenticator.cpp +++ b/GPClient/gatewayauthenticator.cpp @@ -1,14 +1,17 @@ #include +#include +#include #include #include "gatewayauthenticator.h" #include "gphelper.h" #include "loginparams.h" #include "preloginresponse.h" +#include "challengedialog.h" using namespace gpclient::helper; -GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, const GatewayAuthenticatorParams params) +GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params) : QObject() , gateway(gateway) , params(params) @@ -33,6 +36,7 @@ void GatewayAuthenticator::authenticate() loginParams.setUser(params.username()); loginParams.setPassword(params.password()); loginParams.setUserAuthCookie(params.userAuthCookie()); + loginParams.setInputStr(params.inputStr()); login(loginParams); } @@ -48,10 +52,10 @@ void GatewayAuthenticator::login(const LoginParams &loginParams) void GatewayAuthenticator::onLoginFinished() { QNetworkReply *reply = qobject_cast(sender()); - QByteArray response; + QByteArray response = reply->readAll(); - if (reply->error() || (response = reply->readAll()).contains("Authentication failure")) { - PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl).arg(reply->errorString()); + if (reply->error() || response.contains("Authentication failure")) { + PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString()); if (normalLoginWindow) { normalLoginWindow->setProcessing(false); @@ -62,6 +66,12 @@ void GatewayAuthenticator::onLoginFinished() return; } + // 2FA + if (response.contains("Challenge")) { + showChallenge(response); + return; + } + if (normalLoginWindow) { normalLoginWindow->close(); } @@ -83,7 +93,7 @@ void GatewayAuthenticator::onPreloginFinished() QNetworkReply *reply = qobject_cast(sender()); if (reply->error()) { - PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl).arg(reply->errorString()); + PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString()); emit fail("Error occurred on the gateway prelogin interface."); return; @@ -98,7 +108,7 @@ void GatewayAuthenticator::onPreloginFinished() } else if (response.hasNormalAuthFields()) { normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage()); } else { - PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl).arg(QString::fromUtf8(response.rawResponse())); + PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse())); emit fail("Unknown response for gateway prelogin interface."); } @@ -107,7 +117,7 @@ void GatewayAuthenticator::onPreloginFinished() void GatewayAuthenticator::normalAuth(QString labelUsername, QString labelPassword, QString authMessage) { - PLOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername).arg(labelPassword); + PLOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword); normalLoginWindow = new NormalLoginWindow; normalLoginWindow->setPortalAddress(gateway); @@ -179,3 +189,37 @@ void GatewayAuthenticator::onSAMLLoginFail(const QString msg) { emit fail(msg); } + +void GatewayAuthenticator::showChallenge(const QString &responseText) +{ + QRegularExpression re("\"(.*?)\";"); + QRegularExpressionMatchIterator i = re.globalMatch(responseText); + + i.next(); // Skip the status value + QString message = i.next().captured(1); + QString inputStr = i.next().captured(1); + // update the inputSrc field + params.setInputStr(inputStr); + + challengeDialog = new ChallengeDialog; + challengeDialog->setMessage(message); + + connect(challengeDialog, &ChallengeDialog::accepted, this, [this] { + params.setPassword(challengeDialog->getChallenge()); + authenticate(); + }); + + connect(challengeDialog, &ChallengeDialog::rejected, this, [this] { + if (normalLoginWindow) { + normalLoginWindow->close(); + } + emit fail(); + }); + + connect(challengeDialog, &ChallengeDialog::finished, this, [this] { + delete challengeDialog; + challengeDialog = nullptr; + }); + + challengeDialog->show(); +} diff --git a/GPClient/gatewayauthenticator.h b/GPClient/gatewayauthenticator.h index c150a88..df92a31 100644 --- a/GPClient/gatewayauthenticator.h +++ b/GPClient/gatewayauthenticator.h @@ -4,6 +4,7 @@ #include #include "normalloginwindow.h" +#include "challengedialog.h" #include "loginparams.h" #include "gatewayauthenticatorparams.h" @@ -11,7 +12,7 @@ class GatewayAuthenticator : public QObject { Q_OBJECT public: - explicit GatewayAuthenticator(const QString& gateway, const GatewayAuthenticatorParams params); + explicit GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params); ~GatewayAuthenticator(); void authenticate(); @@ -31,16 +32,18 @@ private slots: private: QString gateway; - const GatewayAuthenticatorParams params; + GatewayAuthenticatorParams params; QString preloginUrl; QString loginUrl; NormalLoginWindow *normalLoginWindow{ nullptr }; + ChallengeDialog *challengeDialog{ nullptr }; void login(const LoginParams& loginParams); void doAuth(); void normalAuth(QString labelUsername, QString labelPassword, QString authMessage); void samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl = ""); + void showChallenge(const QString &responseText); }; #endif // GATEWAYAUTHENTICATOR_H diff --git a/GPClient/gatewayauthenticatorparams.cpp b/GPClient/gatewayauthenticatorparams.cpp index 4db1a74..80fce27 100644 --- a/GPClient/gatewayauthenticatorparams.cpp +++ b/GPClient/gatewayauthenticatorparams.cpp @@ -55,3 +55,13 @@ void GatewayAuthenticatorParams::setClientos(const QString &newClientos) m_clientos = newClientos; } +const QString &GatewayAuthenticatorParams::inputStr() const +{ + return m_inputStr; +} + +void GatewayAuthenticatorParams::setInputStr(const QString &inputStr) +{ + m_inputStr = inputStr; +} + diff --git a/GPClient/gatewayauthenticatorparams.h b/GPClient/gatewayauthenticatorparams.h index 72d9176..ec48fc1 100644 --- a/GPClient/gatewayauthenticatorparams.h +++ b/GPClient/gatewayauthenticatorparams.h @@ -24,11 +24,15 @@ public: const QString &clientos() const; void setClientos(const QString &newClientos); + const QString &inputStr() const; + void setInputStr(const QString &inputStr); + private: QString m_username; QString m_password; QString m_userAuthCookie; QString m_clientos; + QString m_inputStr; }; #endif // GATEWAYAUTHENTICATORPARAMS_H diff --git a/GPClient/loginparams.cpp b/GPClient/loginparams.cpp index 5b59fe3..3126231 100644 --- a/GPClient/loginparams.cpp +++ b/GPClient/loginparams.cpp @@ -6,7 +6,7 @@ LoginParams::LoginParams(const QString clientos) { params.addQueryItem("prot", QUrl::toPercentEncoding("https:")); params.addQueryItem("server", ""); - params.addQueryItem("inputSrc", ""); + params.addQueryItem("inputStr", ""); params.addQueryItem("jnlpReady", "jnlpReady"); params.addQueryItem("user", ""); params.addQueryItem("passwd", ""); @@ -61,6 +61,11 @@ void LoginParams::setPreloginCookie(const QString cookie) updateQueryItem("prelogin-cookie", cookie); } +void LoginParams::setInputStr(const QString inputStr) +{ + updateQueryItem("inputStr", inputStr); +} + QByteArray LoginParams::toUtf8() const { return params.toString().toUtf8(); diff --git a/GPClient/loginparams.h b/GPClient/loginparams.h index 87d3d10..1660ad5 100644 --- a/GPClient/loginparams.h +++ b/GPClient/loginparams.h @@ -15,6 +15,7 @@ public: void setUserAuthCookie(const QString cookie); void setPrelogonAuthCookie(const QString cookie); void setPreloginCookie(const QString cookie); + void setInputStr(const QString inputStr); QByteArray toUtf8() const; diff --git a/GPClient/portalauthenticator.cpp b/GPClient/portalauthenticator.cpp index e0900ea..d33ae19 100644 --- a/GPClient/portalauthenticator.cpp +++ b/GPClient/portalauthenticator.cpp @@ -41,7 +41,7 @@ void PortalAuthenticator::onPreloginFinished() QNetworkReply *reply = qobject_cast(sender()); if (reply->error()) { - PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl).arg(reply->errorString()); + PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString()); emit preloginFailed("Error occurred on the portal prelogin interface."); delete reply; return;