Téléverser les fichiers vers "workstation-setup/src"
This commit is contained in:
+285
-906
File diff suppressed because it is too large
Load Diff
@@ -17,17 +17,20 @@
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
constexpr int kHelperTimeoutMs = 60000;
|
||||
constexpr int kHelperTimeoutMs = 120000;
|
||||
|
||||
bool containsLineBreak(const QString &value)
|
||||
{
|
||||
return value.contains(QLatin1Char('\n')) || value.contains(QLatin1Char('\r'));
|
||||
}
|
||||
|
||||
QString homeDirectoryForEffectiveUser()
|
||||
QString effectiveHomeDirectory()
|
||||
{
|
||||
if (passwd *entry = getpwuid(geteuid())) {
|
||||
return QString::fromLocal8Bit(entry->pw_dir);
|
||||
const QString home = QString::fromLocal8Bit(entry->pw_dir);
|
||||
if (!home.isEmpty() && home != QStringLiteral("/")) {
|
||||
return home;
|
||||
}
|
||||
}
|
||||
return QDir::homePath();
|
||||
}
|
||||
@@ -39,12 +42,13 @@ ProvisioningBackend::ProvisioningBackend(QString targetUser, QObject *parent)
|
||||
, m_targetUser(std::move(targetUser))
|
||||
{
|
||||
refreshState();
|
||||
appendLog(QStringLiteral("START currentUser=%1 targetUser=%2 authorized=%3 passwordDone=%4 pinDone=%5")
|
||||
appendLog(QStringLiteral("START currentUser=%1 targetUser=%2 authorized=%3 passwordDone=%4 pinDone=%5 fidoDone=%6")
|
||||
.arg(m_currentUser,
|
||||
m_targetUser,
|
||||
authorizedUser() ? QStringLiteral("yes") : QStringLiteral("no"),
|
||||
m_passwordDone ? QStringLiteral("yes") : QStringLiteral("no"),
|
||||
m_pinDone ? QStringLiteral("yes") : QStringLiteral("no")));
|
||||
m_pinDone ? QStringLiteral("yes") : QStringLiteral("no"),
|
||||
m_fidoDone ? QStringLiteral("yes") : QStringLiteral("no")));
|
||||
}
|
||||
|
||||
ProvisioningBackend::~ProvisioningBackend()
|
||||
@@ -58,25 +62,22 @@ ProvisioningBackend::~ProvisioningBackend()
|
||||
|
||||
QString ProvisioningBackend::effectiveUserName()
|
||||
{
|
||||
const uid_t uid = geteuid();
|
||||
if (passwd *entry = getpwuid(uid)) {
|
||||
if (passwd *entry = getpwuid(geteuid())) {
|
||||
return QString::fromLocal8Bit(entry->pw_name);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
bool ProvisioningBackend::authorizedUser() const
|
||||
{
|
||||
if (m_targetUser.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray targetName = m_targetUser.toLocal8Bit();
|
||||
if (passwd *entry = getpwnam(targetName.constData())) {
|
||||
const QByteArray name = m_targetUser.toLocal8Bit();
|
||||
if (passwd *entry = getpwnam(name.constData())) {
|
||||
return entry->pw_uid == geteuid();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -89,8 +90,7 @@ QString ProvisioningBackend::stateDirectory() const
|
||||
return QDir::cleanPath(candidate + QStringLiteral("/nixos-workstations"));
|
||||
}
|
||||
}
|
||||
|
||||
return QDir::cleanPath(homeDirectoryForEffectiveUser()
|
||||
return QDir::cleanPath(effectiveHomeDirectory()
|
||||
+ QStringLiteral("/.local/state/nixos-workstations"));
|
||||
}
|
||||
|
||||
@@ -109,15 +109,22 @@ QString ProvisioningBackend::pinMarker() const
|
||||
return stateDirectory() + QStringLiteral("/yubikey-pin-created");
|
||||
}
|
||||
|
||||
QString ProvisioningBackend::fidoMarker() const
|
||||
{
|
||||
return stateDirectory() + QStringLiteral("/yubikey-fido-enrolled");
|
||||
}
|
||||
|
||||
void ProvisioningBackend::refreshState()
|
||||
{
|
||||
const bool oldPassword = m_passwordDone;
|
||||
const bool oldPin = m_pinDone;
|
||||
const bool oldFido = m_fidoDone;
|
||||
|
||||
m_passwordDone = QFileInfo::exists(passwordMarker());
|
||||
m_pinDone = QFileInfo::exists(pinMarker());
|
||||
m_fidoDone = QFileInfo::exists(fidoMarker());
|
||||
|
||||
if (oldPassword != m_passwordDone || oldPin != m_pinDone) {
|
||||
if (oldPassword != m_passwordDone || oldPin != m_pinDone || oldFido != m_fidoDone) {
|
||||
emit stateChanged();
|
||||
}
|
||||
}
|
||||
@@ -139,11 +146,10 @@ bool ProvisioningBackend::ensureStateWritable()
|
||||
if (!probe.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool writeOk = probe.write("ok\n") == 3;
|
||||
const bool ok = probe.write("ok\n") == 3;
|
||||
probe.close();
|
||||
probe.remove();
|
||||
return writeOk;
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ProvisioningBackend::createMarker(const QString &path)
|
||||
@@ -157,8 +163,7 @@ bool ProvisioningBackend::createMarker(const QString &path)
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray content =
|
||||
QByteArrayLiteral("completed=")
|
||||
const QByteArray content = QByteArrayLiteral("completed=")
|
||||
+ QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toUtf8()
|
||||
+ '\n';
|
||||
|
||||
@@ -166,14 +171,12 @@ bool ProvisioningBackend::createMarker(const QString &path)
|
||||
marker.cancelWriting();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!marker.commit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return QFile::setPermissions(path,
|
||||
QFileDevice::ReadOwner
|
||||
| QFileDevice::WriteOwner);
|
||||
QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
}
|
||||
|
||||
void ProvisioningBackend::appendLog(const QString &message) const
|
||||
@@ -182,7 +185,6 @@ void ProvisioningBackend::appendLog(const QString &message) const
|
||||
if (!QDir().mkpath(dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile::setPermissions(dir,
|
||||
QFileDevice::ReadOwner
|
||||
| QFileDevice::WriteOwner
|
||||
@@ -193,17 +195,13 @@ void ProvisioningBackend::appendLog(const QString &message) const
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray line =
|
||||
QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs).toUtf8()
|
||||
log.write(QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs).toUtf8()
|
||||
+ QByteArrayLiteral(" ")
|
||||
+ message.toUtf8()
|
||||
+ '\n';
|
||||
|
||||
log.write(line);
|
||||
+ '\n');
|
||||
log.close();
|
||||
QFile::setPermissions(diagnosticLogPath(),
|
||||
QFileDevice::ReadOwner
|
||||
| QFileDevice::WriteOwner);
|
||||
QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
}
|
||||
|
||||
void ProvisioningBackend::setBusy(bool busy)
|
||||
@@ -229,9 +227,30 @@ void ProvisioningBackend::secureClear(QByteArray &data)
|
||||
|
||||
QString ProvisioningBackend::operationName(Operation operation)
|
||||
{
|
||||
return operation == Operation::Password
|
||||
? QStringLiteral("PASSWORD")
|
||||
: QStringLiteral("PIN");
|
||||
switch (operation) {
|
||||
case Operation::Password:
|
||||
return QStringLiteral("PASSWORD");
|
||||
case Operation::Pin:
|
||||
return QStringLiteral("PIN");
|
||||
case Operation::Fido:
|
||||
return QStringLiteral("FIDO");
|
||||
}
|
||||
return QStringLiteral("UNKNOWN");
|
||||
}
|
||||
|
||||
void ProvisioningBackend::emitResult(Operation operation, bool success, const QString &message)
|
||||
{
|
||||
switch (operation) {
|
||||
case Operation::Password:
|
||||
emit passwordChangeFinished(success, message);
|
||||
break;
|
||||
case Operation::Pin:
|
||||
emit pinChangeFinished(success, message);
|
||||
break;
|
||||
case Operation::Fido:
|
||||
emit fidoEnrollmentFinished(success, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProvisioningBackend::changePassword(const QString ¤tPassword,
|
||||
@@ -239,47 +258,31 @@ void ProvisioningBackend::changePassword(const QString ¤tPassword,
|
||||
const QString &confirmation)
|
||||
{
|
||||
if (!authorizedUser()) {
|
||||
appendLog(QStringLiteral("PASSWORD refused: unauthorized user"));
|
||||
emit passwordChangeFinished(false,
|
||||
QStringLiteral("Cette opération n'est pas autorisée pour cet utilisateur."));
|
||||
emit passwordChangeFinished(false, QStringLiteral("Cette opération n'est pas autorisée pour cet utilisateur."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_busy) {
|
||||
emit passwordChangeFinished(false,
|
||||
QStringLiteral("Une opération de configuration est déjà en cours."));
|
||||
emit passwordChangeFinished(false, QStringLiteral("Une opération de configuration est déjà en cours."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_passwordDone) {
|
||||
emit passwordChangeFinished(true,
|
||||
QStringLiteral("Le mot de passe a déjà été personnalisé."));
|
||||
emit passwordChangeFinished(true, QStringLiteral("Le mot de passe a déjà été personnalisé."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentPassword.isEmpty() || newPassword.isEmpty() || confirmation.isEmpty()) {
|
||||
emit passwordChangeFinished(false,
|
||||
QStringLiteral("Tous les champs du mot de passe doivent être renseignés."));
|
||||
emit passwordChangeFinished(false, QStringLiteral("Tous les champs du mot de passe doivent être renseignés."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword != confirmation) {
|
||||
emit passwordChangeFinished(false,
|
||||
QStringLiteral("Les deux nouveaux mots de passe ne correspondent pas."));
|
||||
emit passwordChangeFinished(false, QStringLiteral("Les deux nouveaux mots de passe ne correspondent pas."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (containsLineBreak(currentPassword) || containsLineBreak(newPassword)
|
||||
|| containsLineBreak(confirmation)) {
|
||||
emit passwordChangeFinished(false,
|
||||
QStringLiteral("Le mot de passe contient un caractère non pris en charge."));
|
||||
if (containsLineBreak(currentPassword) || containsLineBreak(newPassword) || containsLineBreak(confirmation)) {
|
||||
emit passwordChangeFinished(false, QStringLiteral("Le mot de passe contient un caractère non pris en charge."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ensureStateWritable()) {
|
||||
appendLog(QStringLiteral("PASSWORD refused: state directory is not writable"));
|
||||
emit passwordChangeFinished(false,
|
||||
QStringLiteral("Impossible d'enregistrer l'état de configuration dans votre profil. Contactez le service informatique."));
|
||||
emit passwordChangeFinished(false, QStringLiteral("Impossible d'enregistrer l'état de configuration dans votre profil."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -294,62 +297,72 @@ void ProvisioningBackend::initializePin(const QString &newPin,
|
||||
const QString &confirmation)
|
||||
{
|
||||
if (!authorizedUser()) {
|
||||
appendLog(QStringLiteral("PIN refused: unauthorized user"));
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Cette opération n'est pas autorisée pour cet utilisateur."));
|
||||
emit pinChangeFinished(false, QStringLiteral("Cette opération n'est pas autorisée pour cet utilisateur."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_busy) {
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Une opération de configuration est déjà en cours."));
|
||||
emit pinChangeFinished(false, QStringLiteral("Une opération de configuration est déjà en cours."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_passwordDone) {
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Le mot de passe doit être personnalisé avant d'initialiser la YubiKey."));
|
||||
emit pinChangeFinished(false, QStringLiteral("Personnalisez d'abord votre mot de passe."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pinDone) {
|
||||
emit pinChangeFinished(true,
|
||||
QStringLiteral("Le PIN FIDO2 de la YubiKey a déjà été initialisé."));
|
||||
emit pinChangeFinished(true, QStringLiteral("Le PIN FIDO2 a déjà été initialisé."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPin.size() < 4 || confirmation.size() < 4) {
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Le PIN doit contenir au moins 4 caractères."));
|
||||
emit pinChangeFinished(false, QStringLiteral("Le PIN doit contenir au moins 4 caractères."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPin != confirmation) {
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Les deux PIN ne correspondent pas."));
|
||||
emit pinChangeFinished(false, QStringLiteral("Les deux PIN ne correspondent pas."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (containsLineBreak(newPin) || containsLineBreak(confirmation)) {
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Le PIN contient un caractère non pris en charge."));
|
||||
emit pinChangeFinished(false, QStringLiteral("Le PIN contient un caractère non pris en charge."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ensureStateWritable()) {
|
||||
appendLog(QStringLiteral("PIN refused: state directory is not writable"));
|
||||
emit pinChangeFinished(false,
|
||||
QStringLiteral("Impossible d'enregistrer l'état de configuration dans votre profil. Contactez le service informatique."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Le helper Étape 1 lit uniquement les deux premières lignes :
|
||||
// nouveau PIN puis confirmation. Aucun ancien PIN n'est transmis.
|
||||
startHelper(Operation::Pin,
|
||||
QString::fromUtf8(NIXOS_WORKSTATIONS_PIN_HELPER_PATH),
|
||||
newPin,
|
||||
confirmation,
|
||||
QString());
|
||||
confirmation);
|
||||
}
|
||||
|
||||
void ProvisioningBackend::enrollFido(const QString ¤tPassword,
|
||||
const QString &pin)
|
||||
{
|
||||
if (!authorizedUser()) {
|
||||
emit fidoEnrollmentFinished(false, QStringLiteral("Cette opération n'est pas autorisée pour cet utilisateur."));
|
||||
return;
|
||||
}
|
||||
if (m_busy) {
|
||||
emit fidoEnrollmentFinished(false, QStringLiteral("Une opération de configuration est déjà en cours."));
|
||||
return;
|
||||
}
|
||||
if (!m_passwordDone || !m_pinDone) {
|
||||
emit fidoEnrollmentFinished(false, QStringLiteral("Le mot de passe et le PIN doivent être configurés avant l'association FIDO2."));
|
||||
return;
|
||||
}
|
||||
if (m_fidoDone) {
|
||||
emit fidoEnrollmentFinished(true, QStringLiteral("La YubiKey est déjà associée au home chiffré."));
|
||||
return;
|
||||
}
|
||||
if (currentPassword.isEmpty() || pin.size() < 4) {
|
||||
emit fidoEnrollmentFinished(false, QStringLiteral("Saisissez votre mot de passe actuel et le PIN de la YubiKey."));
|
||||
return;
|
||||
}
|
||||
if (containsLineBreak(currentPassword) || containsLineBreak(pin)) {
|
||||
emit fidoEnrollmentFinished(false, QStringLiteral("Un des secrets contient un caractère non pris en charge."));
|
||||
return;
|
||||
}
|
||||
|
||||
startHelper(Operation::Fido,
|
||||
QString::fromUtf8(NIXOS_WORKSTATIONS_FIDO_HELPER_PATH),
|
||||
currentPassword,
|
||||
pin);
|
||||
}
|
||||
|
||||
void ProvisioningBackend::startHelper(Operation operation,
|
||||
@@ -360,14 +373,8 @@ void ProvisioningBackend::startHelper(Operation operation,
|
||||
{
|
||||
const QFileInfo helperInfo(helperPath);
|
||||
if (!helperInfo.exists() || !helperInfo.isFile() || !helperInfo.isExecutable()) {
|
||||
appendLog(operationName(operation)
|
||||
+ QStringLiteral(" failed: helper missing or not executable"));
|
||||
const QString message = QStringLiteral("Le composant système requis est introuvable ou inutilisable.");
|
||||
if (operation == Operation::Password) {
|
||||
emit passwordChangeFinished(false, message);
|
||||
} else {
|
||||
emit pinChangeFinished(false, message);
|
||||
}
|
||||
appendLog(operationName(operation) + QStringLiteral(" failed: helper unavailable"));
|
||||
emitResult(operation, false, QStringLiteral("Le composant système requis est introuvable ou inutilisable."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -396,17 +403,16 @@ void ProvisioningBackend::startHelper(Operation operation,
|
||||
process->setProgram(helperPath);
|
||||
process->setProcessChannelMode(QProcess::SeparateChannels);
|
||||
|
||||
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
||||
environment.insert(QStringLiteral("LC_ALL"), QStringLiteral("C"));
|
||||
environment.insert(QStringLiteral("LANG"), QStringLiteral("C"));
|
||||
process->setProcessEnvironment(environment);
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("LC_ALL"), QStringLiteral("C"));
|
||||
env.insert(QStringLiteral("LANG"), QStringLiteral("C"));
|
||||
process->setProcessEnvironment(env);
|
||||
|
||||
auto *timeout = new QTimer(process);
|
||||
timeout->setSingleShot(true);
|
||||
timeout->setInterval(kHelperTimeoutMs);
|
||||
auto *timer = new QTimer(process);
|
||||
timer->setSingleShot(true);
|
||||
timer->setInterval(kHelperTimeoutMs);
|
||||
|
||||
connect(timeout, &QTimer::timeout, process,
|
||||
[this, process, operation]() {
|
||||
connect(timer, &QTimer::timeout, process, [this, process, operation]() {
|
||||
if (m_process != process || process->state() == QProcess::NotRunning) {
|
||||
return;
|
||||
}
|
||||
@@ -415,125 +421,101 @@ void ProvisioningBackend::startHelper(Operation operation,
|
||||
process->kill();
|
||||
});
|
||||
|
||||
connect(process, &QProcess::started, this,
|
||||
[this, process, timeout, operation]() {
|
||||
connect(process, &QProcess::started, this, [this, process, timer, operation]() {
|
||||
if (m_process != process) {
|
||||
return;
|
||||
}
|
||||
|
||||
const qint64 expected = m_pendingPayload.size();
|
||||
const qint64 written = process->write(m_pendingPayload);
|
||||
process->write(m_pendingPayload);
|
||||
process->closeWriteChannel();
|
||||
secureClear(m_pendingPayload);
|
||||
|
||||
if (written != expected) {
|
||||
appendLog(operationName(operation)
|
||||
+ QStringLiteral(" warning: incomplete helper input write"));
|
||||
}
|
||||
|
||||
appendLog(operationName(operation) + QStringLiteral(" helper started"));
|
||||
timeout->start();
|
||||
timer->start();
|
||||
});
|
||||
|
||||
connect(process, &QProcess::errorOccurred, this,
|
||||
[this, process, timeout, operation](QProcess::ProcessError error) {
|
||||
[this, process, timer, operation](QProcess::ProcessError error) {
|
||||
if (m_process != process || error != QProcess::FailedToStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
timeout->stop();
|
||||
timer->stop();
|
||||
secureClear(m_pendingPayload);
|
||||
appendLog(operationName(operation)
|
||||
+ QStringLiteral(" failed to start: ")
|
||||
+ process->errorString());
|
||||
|
||||
m_process = nullptr;
|
||||
process->deleteLater();
|
||||
setBusy(false);
|
||||
|
||||
const QString message = QStringLiteral("Impossible de démarrer le composant de configuration.");
|
||||
if (operation == Operation::Password) {
|
||||
emit passwordChangeFinished(false, message);
|
||||
} else {
|
||||
emit pinChangeFinished(false, message);
|
||||
}
|
||||
appendLog(operationName(operation) + QStringLiteral(" failed to start"));
|
||||
emitResult(operation, false, QStringLiteral("Impossible de démarrer le composant de configuration."));
|
||||
});
|
||||
|
||||
connect(process,
|
||||
qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
|
||||
this,
|
||||
[this, process, timeout, operation](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
[this, process, timer, operation](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
if (m_process != process) {
|
||||
return;
|
||||
}
|
||||
|
||||
timeout->stop();
|
||||
timer->stop();
|
||||
secureClear(m_pendingPayload);
|
||||
|
||||
QByteArray stderrData = process->readAllStandardError();
|
||||
QByteArray stdoutData = process->readAllStandardOutput();
|
||||
QByteArray combinedOutput;
|
||||
combinedOutput.reserve(stderrData.size() + stdoutData.size() + 1);
|
||||
combinedOutput += stderrData;
|
||||
combinedOutput += '\n';
|
||||
combinedOutput += stdoutData;
|
||||
QByteArray combined = stderrData + '\n' + stdoutData;
|
||||
|
||||
const bool timedOut = m_helperTimedOut;
|
||||
const bool helperSucceeded = !timedOut
|
||||
&& exitStatus == QProcess::NormalExit
|
||||
&& exitCode == 0;
|
||||
|
||||
QString message;
|
||||
bool markerCreated = false;
|
||||
QString message;
|
||||
|
||||
if (helperSucceeded) {
|
||||
if (operation == Operation::Password) {
|
||||
switch (operation) {
|
||||
case Operation::Password:
|
||||
markerCreated = createMarker(passwordMarker());
|
||||
message = markerCreated
|
||||
? QStringLiteral("Votre mot de passe personnel est maintenant actif.")
|
||||
: QStringLiteral("Le mot de passe a été modifié, mais l'état local n'a pas pu être enregistré. Contactez le service informatique avant de fermer la session.");
|
||||
} else {
|
||||
? QStringLiteral("Votre nouveau mot de passe protège maintenant votre home chiffré.")
|
||||
: QStringLiteral("Le mot de passe a été modifié mais l'état local n'a pas pu être enregistré.");
|
||||
break;
|
||||
case Operation::Pin:
|
||||
markerCreated = createMarker(pinMarker());
|
||||
message = markerCreated
|
||||
? QStringLiteral("Le PIN FIDO2 de votre YubiKey est maintenant initialisé.")
|
||||
: QStringLiteral("Le PIN a été initialisé, mais l'état local n'a pas pu être enregistré. Contactez le service informatique avant de fermer la session.");
|
||||
: QStringLiteral("Le PIN a été créé mais l'état local n'a pas pu être enregistré.");
|
||||
break;
|
||||
case Operation::Fido:
|
||||
markerCreated = createMarker(fidoMarker());
|
||||
message = markerCreated
|
||||
? QStringLiteral("Votre YubiKey peut maintenant déverrouiller votre home chiffré.")
|
||||
: QStringLiteral("La YubiKey a été enrôlée mais l'état local n'a pas pu être enregistré.");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
message = safeMessageForFailure(operation, exitCode, combinedOutput, timedOut);
|
||||
message = safeMessageForFailure(operation, exitCode, combined, timedOut);
|
||||
}
|
||||
|
||||
QString helperToken = QString::fromUtf8(combinedOutput).trimmed();
|
||||
helperToken.remove(QRegularExpression(QStringLiteral("[^A-Za-z0-9_\\-]")));
|
||||
if (helperToken.size() > 80) {
|
||||
helperToken.truncate(80);
|
||||
}
|
||||
if (helperToken.isEmpty()) {
|
||||
helperToken = QStringLiteral("none");
|
||||
}
|
||||
QString token = QString::fromUtf8(combined).trimmed();
|
||||
token.remove(QRegularExpression(QStringLiteral("[^A-Za-z0-9_\\-]")));
|
||||
if (token.size() > 80) token.truncate(80);
|
||||
if (token.isEmpty()) token = QStringLiteral("none");
|
||||
|
||||
appendLog(operationName(operation)
|
||||
+ QStringLiteral(" helper finished exitCode=%1 normalExit=%2 timeout=%3 marker=%4 token=%5")
|
||||
+ QStringLiteral(" helper finished exitCode=%1 timeout=%2 marker=%3 token=%4")
|
||||
.arg(exitCode)
|
||||
.arg(exitStatus == QProcess::NormalExit ? QStringLiteral("yes") : QStringLiteral("no"))
|
||||
.arg(timedOut ? QStringLiteral("yes") : QStringLiteral("no"))
|
||||
.arg(markerCreated ? QStringLiteral("yes") : QStringLiteral("no"))
|
||||
.arg(helperToken));
|
||||
.arg(token));
|
||||
|
||||
secureClear(stderrData);
|
||||
secureClear(stdoutData);
|
||||
secureClear(combinedOutput);
|
||||
secureClear(combined);
|
||||
|
||||
m_process = nullptr;
|
||||
process->deleteLater();
|
||||
setBusy(false);
|
||||
refreshState();
|
||||
|
||||
const bool completed = helperSucceeded && markerCreated;
|
||||
if (operation == Operation::Password) {
|
||||
emit passwordChangeFinished(completed, message);
|
||||
} else {
|
||||
emit pinChangeFinished(completed, message);
|
||||
}
|
||||
emitResult(operation, helperSucceeded && markerCreated, message);
|
||||
});
|
||||
|
||||
process->start();
|
||||
@@ -547,70 +529,58 @@ QString ProvisioningBackend::safeMessageForFailure(Operation operation,
|
||||
const QString output = QString::fromUtf8(outputData).toLower();
|
||||
|
||||
if (timedOut || exitCode == 124 || output.contains(QStringLiteral("timeout"))) {
|
||||
if (operation == Operation::Password) {
|
||||
if (output.contains(QStringLiteral("timeout_current_prompt"))) {
|
||||
return QStringLiteral("Le système n'a pas présenté l'invite d'authentification du mot de passe actuel.");
|
||||
if (operation == Operation::Fido) {
|
||||
return QStringLiteral("L'association FIDO2 a expiré. Laissez la YubiKey branchée et touchez-la lorsqu'elle clignote.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("timeout_new_prompt"))) {
|
||||
return QStringLiteral("Le mot de passe actuel a été envoyé, mais le système n'a pas demandé le nouveau mot de passe.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("timeout_confirm_prompt"))) {
|
||||
return QStringLiteral("Le nouveau mot de passe a été envoyé, mais le système n'a pas demandé sa confirmation.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("timeout_finish"))) {
|
||||
return QStringLiteral("Le système a reçu les mots de passe mais n'a pas terminé l'opération.");
|
||||
}
|
||||
return QStringLiteral("Le changement de mot de passe a expiré avant la fin de l'opération.");
|
||||
}
|
||||
return QStringLiteral("L'opération YubiKey a expiré. Vérifiez que la clé est connectée puis réessayez.");
|
||||
return QStringLiteral("L'opération a expiré avant de pouvoir être terminée.");
|
||||
}
|
||||
|
||||
if (operation == Operation::Password) {
|
||||
if (output.contains(QStringLiteral("current_rejected"))
|
||||
|| output.contains(QStringLiteral("current_prompt_missing"))
|
||||
|| output.contains(QStringLiteral("current_reprompt"))) {
|
||||
return QStringLiteral("Le mot de passe temporaire actuel est incorrect ou n'a pas pu être vérifié.");
|
||||
if (output.contains(QStringLiteral("current_rejected"))) {
|
||||
return QStringLiteral("Le mot de passe temporaire actuel est incorrect.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("new_rejected"))) {
|
||||
return QStringLiteral("Le système a refusé le nouveau mot de passe. Choisissez-en un autre puis réessayez.");
|
||||
return QStringLiteral("Le nouveau mot de passe a été refusé par la politique de sécurité.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("input_error"))) {
|
||||
return QStringLiteral("Le composant de changement de mot de passe n'a pas reçu les données attendues.");
|
||||
}
|
||||
return QStringLiteral("Le mot de passe n'a pas été modifié. Vérifiez le mot de passe actuel puis réessayez.");
|
||||
return QStringLiteral("Le mot de passe n'a pas pu être modifié par systemd-homed.");
|
||||
}
|
||||
|
||||
if (operation == Operation::Pin) {
|
||||
if (output.contains(QStringLiteral("pin_already_configured"))) {
|
||||
return QStringLiteral("Cette YubiKey possède déjà un PIN FIDO2. Pour l’étape 1, utilisez une clé vierge ou réinitialisée ; aucune tentative de PIN n’a été effectuée.");
|
||||
return QStringLiteral("Cette YubiKey possède déjà un PIN FIDO2. Utilisez une YubiKey vierge ou réinitialisée.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("pin_auth_blocked"))) {
|
||||
return QStringLiteral("Les opérations PIN sont temporairement bloquées. Débranchez puis rebranchez la YubiKey avant toute nouvelle opération.");
|
||||
return QStringLiteral("Les opérations PIN sont temporairement bloquées. Débranchez puis rebranchez la YubiKey.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("pin_blocked"))) {
|
||||
return QStringLiteral("Le PIN FIDO2 de la YubiKey est bloqué. Cette clé n’est pas vierge et ne peut pas être initialisée par l’étape 1.");
|
||||
return QStringLiteral("Le PIN FIDO2 est bloqué. La YubiKey doit être réinitialisée avant provisioning.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("multiple_yubikey"))) {
|
||||
return QStringLiteral("Plusieurs clés FIDO2 sont détectées. Ne laissez branchée que la YubiKey à initialiser puis réessayez.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("no_fido2"))) {
|
||||
return QStringLiteral("La clé détectée ne fournit pas l’interface FIDO2 nécessaire.");
|
||||
return QStringLiteral("Plusieurs clés FIDO2 sont détectées. Ne laissez branchée que la YubiKey d'Alice.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("no_yubikey"))) {
|
||||
return QStringLiteral("Aucune YubiKey FIDO2 compatible n’a été détectée ou le système n’a pas les droits d’accès nécessaires.");
|
||||
return QStringLiteral("Aucune YubiKey FIDO2 compatible n'a été détectée.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("pin_policy"))) {
|
||||
return QStringLiteral("Le PIN choisi ne respecte pas la politique FIDO2 de cette YubiKey.");
|
||||
return QStringLiteral("Le PIN choisi ne respecte pas la politique FIDO2 de la YubiKey.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("pin_state_not_updated"))
|
||||
|| output.contains(QStringLiteral("pin_state_error"))) {
|
||||
return QStringLiteral("La commande FIDO2 a été envoyée, mais l’état PIN retourné par la clé est incohérent. Ne réessayez pas avant vérification avec ykman fido info.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("input_error"))) {
|
||||
return QStringLiteral("Le composant d’initialisation de la YubiKey n’a pas reçu les données attendues.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("pin_helper_error"))) {
|
||||
return QStringLiteral("Le composant FIDO2 a rencontré une erreur interne sans pouvoir confirmer l’initialisation du PIN.");
|
||||
return QStringLiteral("Le PIN FIDO2 n'a pas pu être initialisé.");
|
||||
}
|
||||
|
||||
return QStringLiteral("Le PIN FIDO2 n’a pas pu être initialisé. Vérifiez que la YubiKey est vierge, connectée et compatible FIDO2.");
|
||||
if (output.contains(QStringLiteral("fido_bad_password"))) {
|
||||
return QStringLiteral("Le mot de passe personnel fourni est incorrect ou insuffisant pour mettre à jour le home.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("fido_bad_pin"))) {
|
||||
return QStringLiteral("Le PIN de la YubiKey est incorrect. Ne réessayez pas plusieurs fois : vérifiez le PIN avant une nouvelle tentative.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("multiple_yubikey"))) {
|
||||
return QStringLiteral("Plusieurs clés FIDO2 sont détectées. Ne laissez branchée que la YubiKey d'Alice.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("no_yubikey"))) {
|
||||
return QStringLiteral("Aucune YubiKey FIDO2 compatible n'a été détectée.");
|
||||
}
|
||||
if (output.contains(QStringLiteral("fido_enroll_failed"))) {
|
||||
return QStringLiteral("La YubiKey n'a pas pu être associée au home. Vérifiez le mot de passe, le PIN et touchez la clé lorsqu'elle clignote.");
|
||||
}
|
||||
return QStringLiteral("L'association FIDO2 au home chiffré a échoué.");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ class ProvisioningBackend final : public QObject
|
||||
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||
Q_PROPERTY(bool passwordDone READ passwordDone NOTIFY stateChanged)
|
||||
Q_PROPERTY(bool pinDone READ pinDone NOTIFY stateChanged)
|
||||
Q_PROPERTY(bool fidoDone READ fidoDone NOTIFY stateChanged)
|
||||
Q_PROPERTY(bool complete READ complete NOTIFY stateChanged)
|
||||
Q_PROPERTY(QString currentUser READ currentUser CONSTANT)
|
||||
Q_PROPERTY(QString targetUser READ targetUser CONSTANT)
|
||||
@@ -24,60 +25,66 @@ public:
|
||||
bool busy() const { return m_busy; }
|
||||
bool passwordDone() const { return m_passwordDone; }
|
||||
bool pinDone() const { return m_pinDone; }
|
||||
bool complete() const { return m_passwordDone && m_pinDone; }
|
||||
bool fidoDone() const { return m_fidoDone; }
|
||||
bool complete() const { return m_passwordDone && m_pinDone && m_fidoDone; }
|
||||
QString currentUser() const { return m_currentUser; }
|
||||
QString targetUser() const { return m_targetUser; }
|
||||
bool authorizedUser() const;
|
||||
|
||||
QString stateDirectory() const;
|
||||
QString diagnosticLogPath() const;
|
||||
QString passwordMarker() const;
|
||||
QString pinMarker() const;
|
||||
|
||||
static QString effectiveUserName();
|
||||
|
||||
Q_INVOKABLE void changePassword(const QString ¤tPassword,
|
||||
const QString &newPassword,
|
||||
const QString &confirmation);
|
||||
|
||||
Q_INVOKABLE void initializePin(const QString &newPin,
|
||||
const QString &confirmation);
|
||||
Q_INVOKABLE void enrollFido(const QString ¤tPassword,
|
||||
const QString &pin);
|
||||
|
||||
signals:
|
||||
void busyChanged();
|
||||
void stateChanged();
|
||||
void passwordChangeFinished(bool success, const QString &message);
|
||||
void pinChangeFinished(bool success, const QString &message);
|
||||
void fidoEnrollmentFinished(bool success, const QString &message);
|
||||
|
||||
private:
|
||||
enum class Operation {
|
||||
Password,
|
||||
Pin
|
||||
Pin,
|
||||
Fido
|
||||
};
|
||||
|
||||
QString stateDirectory() const;
|
||||
QString diagnosticLogPath() const;
|
||||
QString passwordMarker() const;
|
||||
QString pinMarker() const;
|
||||
QString fidoMarker() const;
|
||||
|
||||
static QString effectiveUserName();
|
||||
static void secureClear(QByteArray &data);
|
||||
static QString operationName(Operation operation);
|
||||
|
||||
void refreshState();
|
||||
bool ensureStateWritable();
|
||||
bool createMarker(const QString &path);
|
||||
void appendLog(const QString &message) const;
|
||||
void setBusy(bool busy);
|
||||
|
||||
void startHelper(Operation operation,
|
||||
const QString &helperPath,
|
||||
const QString &firstSecret,
|
||||
const QString &secondSecret,
|
||||
const QString &thirdSecret);
|
||||
|
||||
void setBusy(bool busy);
|
||||
void refreshState();
|
||||
bool ensureStateWritable();
|
||||
bool createMarker(const QString &path);
|
||||
void appendLog(const QString &message) const;
|
||||
const QString &thirdSecret = {});
|
||||
|
||||
void emitResult(Operation operation, bool success, const QString &message);
|
||||
QString safeMessageForFailure(Operation operation,
|
||||
int exitCode,
|
||||
const QByteArray &output,
|
||||
bool timedOut) const;
|
||||
|
||||
static void secureClear(QByteArray &data);
|
||||
static QString operationName(Operation operation);
|
||||
|
||||
bool m_busy = false;
|
||||
bool m_passwordDone = false;
|
||||
bool m_pinDone = false;
|
||||
bool m_fidoDone = false;
|
||||
bool m_helperTimedOut = false;
|
||||
QString m_currentUser;
|
||||
QString m_targetUser;
|
||||
|
||||
Reference in New Issue
Block a user