Téléverser les fichiers vers "workstation-setup/src"

This commit is contained in:
2026-08-19 15:24:29 +02:00
parent d47fe70c5b
commit 16ebd52f79
3 changed files with 641 additions and 1285 deletions
File diff suppressed because it is too large Load Diff
+186 -216
View File
@@ -17,17 +17,20 @@
#include <utility> #include <utility>
namespace { namespace {
constexpr int kHelperTimeoutMs = 60000; constexpr int kHelperTimeoutMs = 120000;
bool containsLineBreak(const QString &value) bool containsLineBreak(const QString &value)
{ {
return value.contains(QLatin1Char('\n')) || value.contains(QLatin1Char('\r')); return value.contains(QLatin1Char('\n')) || value.contains(QLatin1Char('\r'));
} }
QString homeDirectoryForEffectiveUser() QString effectiveHomeDirectory()
{ {
if (passwd *entry = getpwuid(geteuid())) { 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(); return QDir::homePath();
} }
@@ -39,12 +42,13 @@ ProvisioningBackend::ProvisioningBackend(QString targetUser, QObject *parent)
, m_targetUser(std::move(targetUser)) , m_targetUser(std::move(targetUser))
{ {
refreshState(); 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, .arg(m_currentUser,
m_targetUser, m_targetUser,
authorizedUser() ? QStringLiteral("yes") : QStringLiteral("no"), authorizedUser() ? QStringLiteral("yes") : QStringLiteral("no"),
m_passwordDone ? 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() ProvisioningBackend::~ProvisioningBackend()
@@ -58,25 +62,22 @@ ProvisioningBackend::~ProvisioningBackend()
QString ProvisioningBackend::effectiveUserName() QString ProvisioningBackend::effectiveUserName()
{ {
const uid_t uid = geteuid(); if (passwd *entry = getpwuid(geteuid())) {
if (passwd *entry = getpwuid(uid)) {
return QString::fromLocal8Bit(entry->pw_name); return QString::fromLocal8Bit(entry->pw_name);
} }
return {}; return {};
} }
bool ProvisioningBackend::authorizedUser() const bool ProvisioningBackend::authorizedUser() const
{ {
if (m_targetUser.isEmpty()) { if (m_targetUser.isEmpty()) {
return false; return false;
} }
const QByteArray targetName = m_targetUser.toLocal8Bit(); const QByteArray name = m_targetUser.toLocal8Bit();
if (passwd *entry = getpwnam(targetName.constData())) { if (passwd *entry = getpwnam(name.constData())) {
return entry->pw_uid == geteuid(); return entry->pw_uid == geteuid();
} }
return false; return false;
} }
@@ -89,8 +90,7 @@ QString ProvisioningBackend::stateDirectory() const
return QDir::cleanPath(candidate + QStringLiteral("/nixos-workstations")); return QDir::cleanPath(candidate + QStringLiteral("/nixos-workstations"));
} }
} }
return QDir::cleanPath(effectiveHomeDirectory()
return QDir::cleanPath(homeDirectoryForEffectiveUser()
+ QStringLiteral("/.local/state/nixos-workstations")); + QStringLiteral("/.local/state/nixos-workstations"));
} }
@@ -109,15 +109,22 @@ QString ProvisioningBackend::pinMarker() const
return stateDirectory() + QStringLiteral("/yubikey-pin-created"); return stateDirectory() + QStringLiteral("/yubikey-pin-created");
} }
QString ProvisioningBackend::fidoMarker() const
{
return stateDirectory() + QStringLiteral("/yubikey-fido-enrolled");
}
void ProvisioningBackend::refreshState() void ProvisioningBackend::refreshState()
{ {
const bool oldPassword = m_passwordDone; const bool oldPassword = m_passwordDone;
const bool oldPin = m_pinDone; const bool oldPin = m_pinDone;
const bool oldFido = m_fidoDone;
m_passwordDone = QFileInfo::exists(passwordMarker()); m_passwordDone = QFileInfo::exists(passwordMarker());
m_pinDone = QFileInfo::exists(pinMarker()); 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(); emit stateChanged();
} }
} }
@@ -139,11 +146,10 @@ bool ProvisioningBackend::ensureStateWritable()
if (!probe.open(QIODevice::WriteOnly | QIODevice::Truncate)) { if (!probe.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return false; return false;
} }
const bool ok = probe.write("ok\n") == 3;
const bool writeOk = probe.write("ok\n") == 3;
probe.close(); probe.close();
probe.remove(); probe.remove();
return writeOk; return ok;
} }
bool ProvisioningBackend::createMarker(const QString &path) bool ProvisioningBackend::createMarker(const QString &path)
@@ -157,8 +163,7 @@ bool ProvisioningBackend::createMarker(const QString &path)
return false; return false;
} }
const QByteArray content = const QByteArray content = QByteArrayLiteral("completed=")
QByteArrayLiteral("completed=")
+ QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toUtf8() + QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toUtf8()
+ '\n'; + '\n';
@@ -166,14 +171,12 @@ bool ProvisioningBackend::createMarker(const QString &path)
marker.cancelWriting(); marker.cancelWriting();
return false; return false;
} }
if (!marker.commit()) { if (!marker.commit()) {
return false; return false;
} }
return QFile::setPermissions(path, return QFile::setPermissions(path,
QFileDevice::ReadOwner QFileDevice::ReadOwner | QFileDevice::WriteOwner);
| QFileDevice::WriteOwner);
} }
void ProvisioningBackend::appendLog(const QString &message) const void ProvisioningBackend::appendLog(const QString &message) const
@@ -182,7 +185,6 @@ void ProvisioningBackend::appendLog(const QString &message) const
if (!QDir().mkpath(dir)) { if (!QDir().mkpath(dir)) {
return; return;
} }
QFile::setPermissions(dir, QFile::setPermissions(dir,
QFileDevice::ReadOwner QFileDevice::ReadOwner
| QFileDevice::WriteOwner | QFileDevice::WriteOwner
@@ -193,17 +195,13 @@ void ProvisioningBackend::appendLog(const QString &message) const
return; return;
} }
const QByteArray line = log.write(QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs).toUtf8()
QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs).toUtf8() + QByteArrayLiteral(" ")
+ QByteArrayLiteral(" ") + message.toUtf8()
+ message.toUtf8() + '\n');
+ '\n';
log.write(line);
log.close(); log.close();
QFile::setPermissions(diagnosticLogPath(), QFile::setPermissions(diagnosticLogPath(),
QFileDevice::ReadOwner QFileDevice::ReadOwner | QFileDevice::WriteOwner);
| QFileDevice::WriteOwner);
} }
void ProvisioningBackend::setBusy(bool busy) void ProvisioningBackend::setBusy(bool busy)
@@ -229,9 +227,30 @@ void ProvisioningBackend::secureClear(QByteArray &data)
QString ProvisioningBackend::operationName(Operation operation) QString ProvisioningBackend::operationName(Operation operation)
{ {
return operation == Operation::Password switch (operation) {
? QStringLiteral("PASSWORD") case Operation::Password:
: QStringLiteral("PIN"); 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 &currentPassword, void ProvisioningBackend::changePassword(const QString &currentPassword,
@@ -239,47 +258,31 @@ void ProvisioningBackend::changePassword(const QString &currentPassword,
const QString &confirmation) const QString &confirmation)
{ {
if (!authorizedUser()) { 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; return;
} }
if (m_busy) { if (m_busy) {
emit passwordChangeFinished(false, emit passwordChangeFinished(false, QStringLiteral("Une opération de configuration est déjà en cours."));
QStringLiteral("Une opération de configuration est déjà en cours."));
return; return;
} }
if (m_passwordDone) { if (m_passwordDone) {
emit passwordChangeFinished(true, emit passwordChangeFinished(true, QStringLiteral("Le mot de passe a déjà été personnalisé."));
QStringLiteral("Le mot de passe a déjà été personnalisé."));
return; return;
} }
if (currentPassword.isEmpty() || newPassword.isEmpty() || confirmation.isEmpty()) { if (currentPassword.isEmpty() || newPassword.isEmpty() || confirmation.isEmpty()) {
emit passwordChangeFinished(false, emit passwordChangeFinished(false, QStringLiteral("Tous les champs du mot de passe doivent être renseignés."));
QStringLiteral("Tous les champs du mot de passe doivent être renseignés."));
return; return;
} }
if (newPassword != confirmation) { if (newPassword != confirmation) {
emit passwordChangeFinished(false, emit passwordChangeFinished(false, QStringLiteral("Les deux nouveaux mots de passe ne correspondent pas."));
QStringLiteral("Les deux nouveaux mots de passe ne correspondent pas."));
return; return;
} }
if (containsLineBreak(currentPassword) || containsLineBreak(newPassword) || containsLineBreak(confirmation)) {
if (containsLineBreak(currentPassword) || containsLineBreak(newPassword) emit passwordChangeFinished(false, QStringLiteral("Le mot de passe contient un caractère non pris en charge."));
|| containsLineBreak(confirmation)) {
emit passwordChangeFinished(false,
QStringLiteral("Le mot de passe contient un caractère non pris en charge."));
return; return;
} }
if (!ensureStateWritable()) { 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."));
emit passwordChangeFinished(false,
QStringLiteral("Impossible d'enregistrer l'état de configuration dans votre profil. Contactez le service informatique."));
return; return;
} }
@@ -291,65 +294,75 @@ void ProvisioningBackend::changePassword(const QString &currentPassword,
} }
void ProvisioningBackend::initializePin(const QString &newPin, void ProvisioningBackend::initializePin(const QString &newPin,
const QString &confirmation) const QString &confirmation)
{ {
if (!authorizedUser()) { 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; return;
} }
if (m_busy) { if (m_busy) {
emit pinChangeFinished(false, emit pinChangeFinished(false, QStringLiteral("Une opération de configuration est déjà en cours."));
QStringLiteral("Une opération de configuration est déjà en cours."));
return; return;
} }
if (!m_passwordDone) { if (!m_passwordDone) {
emit pinChangeFinished(false, emit pinChangeFinished(false, QStringLiteral("Personnalisez d'abord votre mot de passe."));
QStringLiteral("Le mot de passe doit être personnalisé avant d'initialiser la YubiKey."));
return; return;
} }
if (m_pinDone) { if (m_pinDone) {
emit pinChangeFinished(true, emit pinChangeFinished(true, QStringLiteral("Le PIN FIDO2 a déjà été initialisé."));
QStringLiteral("Le PIN FIDO2 de la YubiKey a déjà été initialisé."));
return; return;
} }
if (newPin.size() < 4 || confirmation.size() < 4) { if (newPin.size() < 4 || confirmation.size() < 4) {
emit pinChangeFinished(false, emit pinChangeFinished(false, QStringLiteral("Le PIN doit contenir au moins 4 caractères."));
QStringLiteral("Le PIN doit contenir au moins 4 caractères."));
return; return;
} }
if (newPin != confirmation) { if (newPin != confirmation) {
emit pinChangeFinished(false, emit pinChangeFinished(false, QStringLiteral("Les deux PIN ne correspondent pas."));
QStringLiteral("Les deux PIN ne correspondent pas."));
return; return;
} }
if (containsLineBreak(newPin) || containsLineBreak(confirmation)) { if (containsLineBreak(newPin) || containsLineBreak(confirmation)) {
emit pinChangeFinished(false, emit pinChangeFinished(false, QStringLiteral("Le PIN contient un caractère non pris en charge."));
QStringLiteral("Le PIN contient un caractère non pris en charge."));
return; 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, startHelper(Operation::Pin,
QString::fromUtf8(NIXOS_WORKSTATIONS_PIN_HELPER_PATH), QString::fromUtf8(NIXOS_WORKSTATIONS_PIN_HELPER_PATH),
newPin, newPin,
confirmation, confirmation);
QString()); }
void ProvisioningBackend::enrollFido(const QString &currentPassword,
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, void ProvisioningBackend::startHelper(Operation operation,
@@ -360,14 +373,8 @@ void ProvisioningBackend::startHelper(Operation operation,
{ {
const QFileInfo helperInfo(helperPath); const QFileInfo helperInfo(helperPath);
if (!helperInfo.exists() || !helperInfo.isFile() || !helperInfo.isExecutable()) { if (!helperInfo.exists() || !helperInfo.isFile() || !helperInfo.isExecutable()) {
appendLog(operationName(operation) appendLog(operationName(operation) + QStringLiteral(" failed: helper unavailable"));
+ QStringLiteral(" failed: helper missing or not executable")); emitResult(operation, false, QStringLiteral("Le composant système requis est introuvable ou inutilisable."));
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);
}
return; return;
} }
@@ -396,17 +403,16 @@ void ProvisioningBackend::startHelper(Operation operation,
process->setProgram(helperPath); process->setProgram(helperPath);
process->setProcessChannelMode(QProcess::SeparateChannels); process->setProcessChannelMode(QProcess::SeparateChannels);
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("LC_ALL"), QStringLiteral("C")); env.insert(QStringLiteral("LC_ALL"), QStringLiteral("C"));
environment.insert(QStringLiteral("LANG"), QStringLiteral("C")); env.insert(QStringLiteral("LANG"), QStringLiteral("C"));
process->setProcessEnvironment(environment); process->setProcessEnvironment(env);
auto *timeout = new QTimer(process); auto *timer = new QTimer(process);
timeout->setSingleShot(true); timer->setSingleShot(true);
timeout->setInterval(kHelperTimeoutMs); timer->setInterval(kHelperTimeoutMs);
connect(timeout, &QTimer::timeout, process, connect(timer, &QTimer::timeout, process, [this, process, operation]() {
[this, process, operation]() {
if (m_process != process || process->state() == QProcess::NotRunning) { if (m_process != process || process->state() == QProcess::NotRunning) {
return; return;
} }
@@ -415,125 +421,101 @@ void ProvisioningBackend::startHelper(Operation operation,
process->kill(); process->kill();
}); });
connect(process, &QProcess::started, this, connect(process, &QProcess::started, this, [this, process, timer, operation]() {
[this, process, timeout, operation]() {
if (m_process != process) { if (m_process != process) {
return; return;
} }
process->write(m_pendingPayload);
const qint64 expected = m_pendingPayload.size();
const qint64 written = process->write(m_pendingPayload);
process->closeWriteChannel(); process->closeWriteChannel();
secureClear(m_pendingPayload); secureClear(m_pendingPayload);
if (written != expected) {
appendLog(operationName(operation)
+ QStringLiteral(" warning: incomplete helper input write"));
}
appendLog(operationName(operation) + QStringLiteral(" helper started")); appendLog(operationName(operation) + QStringLiteral(" helper started"));
timeout->start(); timer->start();
}); });
connect(process, &QProcess::errorOccurred, this, 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) { if (m_process != process || error != QProcess::FailedToStart) {
return; return;
} }
timer->stop();
timeout->stop();
secureClear(m_pendingPayload); secureClear(m_pendingPayload);
appendLog(operationName(operation)
+ QStringLiteral(" failed to start: ")
+ process->errorString());
m_process = nullptr; m_process = nullptr;
process->deleteLater(); process->deleteLater();
setBusy(false); setBusy(false);
appendLog(operationName(operation) + QStringLiteral(" failed to start"));
const QString message = QStringLiteral("Impossible de démarrer le composant de configuration."); emitResult(operation, false, QStringLiteral("Impossible de démarrer le composant de configuration."));
if (operation == Operation::Password) {
emit passwordChangeFinished(false, message);
} else {
emit pinChangeFinished(false, message);
}
}); });
connect(process, connect(process,
qOverload<int, QProcess::ExitStatus>(&QProcess::finished), qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
this, this,
[this, process, timeout, operation](int exitCode, QProcess::ExitStatus exitStatus) { [this, process, timer, operation](int exitCode, QProcess::ExitStatus exitStatus) {
if (m_process != process) { if (m_process != process) {
return; return;
} }
timeout->stop(); timer->stop();
secureClear(m_pendingPayload); secureClear(m_pendingPayload);
QByteArray stderrData = process->readAllStandardError(); QByteArray stderrData = process->readAllStandardError();
QByteArray stdoutData = process->readAllStandardOutput(); QByteArray stdoutData = process->readAllStandardOutput();
QByteArray combinedOutput; QByteArray combined = stderrData + '\n' + stdoutData;
combinedOutput.reserve(stderrData.size() + stdoutData.size() + 1);
combinedOutput += stderrData;
combinedOutput += '\n';
combinedOutput += stdoutData;
const bool timedOut = m_helperTimedOut; const bool timedOut = m_helperTimedOut;
const bool helperSucceeded = !timedOut const bool helperSucceeded = !timedOut
&& exitStatus == QProcess::NormalExit && exitStatus == QProcess::NormalExit
&& exitCode == 0; && exitCode == 0;
QString message;
bool markerCreated = false; bool markerCreated = false;
QString message;
if (helperSucceeded) { if (helperSucceeded) {
if (operation == Operation::Password) { switch (operation) {
case Operation::Password:
markerCreated = createMarker(passwordMarker()); markerCreated = createMarker(passwordMarker());
message = markerCreated message = markerCreated
? QStringLiteral("Votre mot de passe personnel est maintenant actif.") ? 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é. Contactez le service informatique avant de fermer la session."); : QStringLiteral("Le mot de passe a été modifié mais l'état local n'a pas pu être enregistré.");
} else { break;
case Operation::Pin:
markerCreated = createMarker(pinMarker()); markerCreated = createMarker(pinMarker());
message = markerCreated message = markerCreated
? QStringLiteral("Le PIN FIDO2 de votre YubiKey est maintenant initialisé.") ? 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 { } else {
message = safeMessageForFailure(operation, exitCode, combinedOutput, timedOut); message = safeMessageForFailure(operation, exitCode, combined, timedOut);
} }
QString helperToken = QString::fromUtf8(combinedOutput).trimmed(); QString token = QString::fromUtf8(combined).trimmed();
helperToken.remove(QRegularExpression(QStringLiteral("[^A-Za-z0-9_\\-]"))); token.remove(QRegularExpression(QStringLiteral("[^A-Za-z0-9_\\-]")));
if (helperToken.size() > 80) { if (token.size() > 80) token.truncate(80);
helperToken.truncate(80); if (token.isEmpty()) token = QStringLiteral("none");
}
if (helperToken.isEmpty()) {
helperToken = QStringLiteral("none");
}
appendLog(operationName(operation) 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(exitCode)
.arg(exitStatus == QProcess::NormalExit ? QStringLiteral("yes") : QStringLiteral("no"))
.arg(timedOut ? QStringLiteral("yes") : QStringLiteral("no")) .arg(timedOut ? QStringLiteral("yes") : QStringLiteral("no"))
.arg(markerCreated ? QStringLiteral("yes") : QStringLiteral("no")) .arg(markerCreated ? QStringLiteral("yes") : QStringLiteral("no"))
.arg(helperToken)); .arg(token));
secureClear(stderrData); secureClear(stderrData);
secureClear(stdoutData); secureClear(stdoutData);
secureClear(combinedOutput); secureClear(combined);
m_process = nullptr; m_process = nullptr;
process->deleteLater(); process->deleteLater();
setBusy(false); setBusy(false);
refreshState(); refreshState();
const bool completed = helperSucceeded && markerCreated; emitResult(operation, helperSucceeded && markerCreated, message);
if (operation == Operation::Password) {
emit passwordChangeFinished(completed, message);
} else {
emit pinChangeFinished(completed, message);
}
}); });
process->start(); process->start();
@@ -547,70 +529,58 @@ QString ProvisioningBackend::safeMessageForFailure(Operation operation,
const QString output = QString::fromUtf8(outputData).toLower(); const QString output = QString::fromUtf8(outputData).toLower();
if (timedOut || exitCode == 124 || output.contains(QStringLiteral("timeout"))) { if (timedOut || exitCode == 124 || output.contains(QStringLiteral("timeout"))) {
if (operation == Operation::Password) { if (operation == Operation::Fido) {
if (output.contains(QStringLiteral("timeout_current_prompt"))) { return QStringLiteral("L'association FIDO2 a expiré. Laissez la YubiKey branchée et touchez-la lorsqu'elle clignote.");
return QStringLiteral("Le système n'a pas présenté l'invite d'authentification du mot de passe actuel.");
}
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 (operation == Operation::Password) {
if (output.contains(QStringLiteral("current_rejected")) if (output.contains(QStringLiteral("current_rejected"))) {
|| output.contains(QStringLiteral("current_prompt_missing")) return QStringLiteral("Le mot de passe temporaire actuel est incorrect.");
|| 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("new_rejected"))) { 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 mot de passe n'a pas pu être modifié par systemd-homed.");
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.");
} }
if (output.contains(QStringLiteral("pin_already_configured"))) { if (operation == Operation::Pin) {
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 na été effectuée."); if (output.contains(QStringLiteral("pin_already_configured"))) {
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.");
}
if (output.contains(QStringLiteral("pin_blocked"))) {
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 d'Alice.");
}
if (output.contains(QStringLiteral("no_yubikey"))) {
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 la YubiKey.");
}
return QStringLiteral("Le PIN FIDO2 n'a pas pu être initialisé.");
} }
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."); 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("pin_blocked"))) { if (output.contains(QStringLiteral("fido_bad_pin"))) {
return QStringLiteral("Le PIN FIDO2 de la YubiKey est bloqué. Cette clé nest pas vierge et ne peut pas être initialisée par l’étape 1."); 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"))) { 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."); return QStringLiteral("Plusieurs clés FIDO2 sont détectées. Ne laissez branchée que la YubiKey d'Alice.");
}
if (output.contains(QStringLiteral("no_fido2"))) {
return QStringLiteral("La clé détectée ne fournit pas linterface FIDO2 nécessaire.");
} }
if (output.contains(QStringLiteral("no_yubikey"))) { if (output.contains(QStringLiteral("no_yubikey"))) {
return QStringLiteral("Aucune YubiKey FIDO2 compatible na été détectée ou le système na pas les droits daccès nécessaires."); return QStringLiteral("Aucune YubiKey FIDO2 compatible n'a été détectée.");
} }
if (output.contains(QStringLiteral("pin_policy"))) { if (output.contains(QStringLiteral("fido_enroll_failed"))) {
return QStringLiteral("Le PIN choisi ne respecte pas la politique FIDO2 de cette YubiKey."); 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.");
} }
if (output.contains(QStringLiteral("pin_state_not_updated")) return QStringLiteral("L'association FIDO2 au home chiffré a échoué.");
|| 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 dinitialisation de la YubiKey na 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 linitialisation du PIN.");
}
return QStringLiteral("Le PIN FIDO2 na pas pu être initialisé. Vérifiez que la YubiKey est vierge, connectée et compatible FIDO2.");
} }
+27 -20
View File
@@ -12,6 +12,7 @@ class ProvisioningBackend final : public QObject
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
Q_PROPERTY(bool passwordDone READ passwordDone NOTIFY stateChanged) Q_PROPERTY(bool passwordDone READ passwordDone NOTIFY stateChanged)
Q_PROPERTY(bool pinDone READ pinDone 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(bool complete READ complete NOTIFY stateChanged)
Q_PROPERTY(QString currentUser READ currentUser CONSTANT) Q_PROPERTY(QString currentUser READ currentUser CONSTANT)
Q_PROPERTY(QString targetUser READ targetUser CONSTANT) Q_PROPERTY(QString targetUser READ targetUser CONSTANT)
@@ -24,60 +25,66 @@ public:
bool busy() const { return m_busy; } bool busy() const { return m_busy; }
bool passwordDone() const { return m_passwordDone; } bool passwordDone() const { return m_passwordDone; }
bool pinDone() const { return m_pinDone; } 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 currentUser() const { return m_currentUser; }
QString targetUser() const { return m_targetUser; } QString targetUser() const { return m_targetUser; }
bool authorizedUser() const; bool authorizedUser() const;
QString stateDirectory() const;
QString diagnosticLogPath() const;
QString passwordMarker() const;
QString pinMarker() const;
static QString effectiveUserName();
Q_INVOKABLE void changePassword(const QString &currentPassword, Q_INVOKABLE void changePassword(const QString &currentPassword,
const QString &newPassword, const QString &newPassword,
const QString &confirmation); const QString &confirmation);
Q_INVOKABLE void initializePin(const QString &newPin, Q_INVOKABLE void initializePin(const QString &newPin,
const QString &confirmation); const QString &confirmation);
Q_INVOKABLE void enrollFido(const QString &currentPassword,
const QString &pin);
signals: signals:
void busyChanged(); void busyChanged();
void stateChanged(); void stateChanged();
void passwordChangeFinished(bool success, const QString &message); void passwordChangeFinished(bool success, const QString &message);
void pinChangeFinished(bool success, const QString &message); void pinChangeFinished(bool success, const QString &message);
void fidoEnrollmentFinished(bool success, const QString &message);
private: private:
enum class Operation { enum class Operation {
Password, 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, void startHelper(Operation operation,
const QString &helperPath, const QString &helperPath,
const QString &firstSecret, const QString &firstSecret,
const QString &secondSecret, const QString &secondSecret,
const QString &thirdSecret); const QString &thirdSecret = {});
void setBusy(bool busy);
void refreshState();
bool ensureStateWritable();
bool createMarker(const QString &path);
void appendLog(const QString &message) const;
void emitResult(Operation operation, bool success, const QString &message);
QString safeMessageForFailure(Operation operation, QString safeMessageForFailure(Operation operation,
int exitCode, int exitCode,
const QByteArray &output, const QByteArray &output,
bool timedOut) const; bool timedOut) const;
static void secureClear(QByteArray &data);
static QString operationName(Operation operation);
bool m_busy = false; bool m_busy = false;
bool m_passwordDone = false; bool m_passwordDone = false;
bool m_pinDone = false; bool m_pinDone = false;
bool m_fidoDone = false;
bool m_helperTimedOut = false; bool m_helperTimedOut = false;
QString m_currentUser; QString m_currentUser;
QString m_targetUser; QString m_targetUser;