feat(sudo): print warning if Windows Sudo is misconfigured
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -2919,6 +2919,7 @@ dependencies = [
|
||||
"which",
|
||||
"wildmatch",
|
||||
"windows 0.62.0",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3428,6 +3429,17 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f91f87ce112ffb7275000ea98eb1940912c21c1567c9312fde20261f3eadd29"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-result 0.4.0",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
|
||||
@@ -82,6 +82,7 @@ is_elevated = "~0.1"
|
||||
parselnk = "~0.1"
|
||||
self_update_crate = { version = "~0.40", default-features = false, optional = true, package = "self_update", features = ["archive-zip", "compression-zip-deflate", "rustls"] }
|
||||
windows = { version = "~0.62", features = ["Win32_System_Console"] }
|
||||
windows-registry = "~0.6"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
@@ -1170,14 +1170,54 @@ _version: 2
|
||||
zh_CN: "请安装 `sudo`、`doas`、`pkexec`、`run0` 或 `please` 之一来运行这些步骤。"
|
||||
zh_TW: "請安裝 `sudo`、`doas`、`pkexec`、`run0` 或 `please` 之一來執行這些步驟。"
|
||||
de: "Installieren Sie `sudo`, `doas`, `pkexec`, `run0` oder `please`, um diese Schritte auszuführen."
|
||||
"Install gsudo or enable Windows Sudo to run these steps.":
|
||||
en: "Install gsudo or enable Windows Sudo to run these steps."
|
||||
lt: "Įdiekite gsudo arba įjunkite Windows Sudo, kad vykdytumėte šiuos veiksmus."
|
||||
es: "Instale gsudo o habilite Windows Sudo para ejecutar estos pasos."
|
||||
fr: "Installez gsudo ou activez Windows Sudo pour exécuter ces étapes."
|
||||
zh_CN: "请安装 gsudo 或启用 Windows Sudo 来运行这些步骤。"
|
||||
zh_TW: "請安裝 gsudo 或啟用 Windows Sudo 來執行這些步驟。"
|
||||
de: "Installieren Sie gsudo oder aktivieren Sie Windows Sudo, um diese Schritte auszuführen."
|
||||
"Install gsudo to run these steps.":
|
||||
en: "Install gsudo to run these steps."
|
||||
lt: "Įdiekite gsudo, kad vykdytumėte šiuos veiksmus."
|
||||
es: "Instale gsudo para ejecutar estos pasos."
|
||||
fr: "Installez gsudo pour exécuter ces étapes."
|
||||
zh_CN: "请安装 gsudo 来运行这些步骤。"
|
||||
zh_TW: "請安裝 gsudo 來執行這些步驟。"
|
||||
de: "Installieren Sie gsudo, um diese Schritte auszuführen."
|
||||
"Install gsudo or enable Windows Sudo to run these steps.\nFor Windows Sudo, the default 'In a new window' mode is not supported as it prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more.":
|
||||
en: "Install gsudo or enable Windows Sudo to run these steps.\nFor Windows Sudo, the default 'In a new window' mode is not supported as it prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more."
|
||||
lt: "Įdiekite gsudo arba įjunkite Windows Sudo, kad vykdytumėte šiuos veiksmus.\nWindows Sudo numatytasis „Naujo lango“ režimas nepalaikomas, nes jis neleidžia Topgrade laukti, kol komandos bus baigtos. Prašome nustatyti „Inline“ režimą.\nDaugiau sužinokite adresu https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
es: "Instale gsudo o habilite Windows Sudo para ejecutar estos pasos.\nEn Windows Sudo, el modo predeterminado 'En una nueva ventana' no es compatible porque impide que Topgrade espere a que los comandos terminen. Por favor, configúrelo en modo 'Inline'.\nMás información en https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
fr: "Installez gsudo ou activez Windows Sudo pour exécuter ces étapes.\nAvec Windows Sudo, le mode par défaut « Dans une nouvelle fenêtre » n’est pas pris en charge car il empêche Topgrade d’attendre la fin des commandes. Veuillez le configurer en mode « Inline ».\nEn savoir plus sur https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
zh_CN: "请安装 gsudo 或启用 Windows Sudo 来运行这些步骤。\n在 Windows Sudo 中,默认的“新窗口”模式不受支持,因为它会阻止 Topgrade 等待命令完成。请将其配置为“内联”模式。\n了解更多信息:https://go.microsoft.com/fwlink/?linkid=2257346"
|
||||
zh_TW: "請安裝 gsudo 或啟用 Windows Sudo 來執行這些步驟。\n在 Windows Sudo 中,預設的「新視窗」模式不受支援,因為它會阻止 Topgrade 等待命令完成。請將其設定為「內嵌」模式。\n了解更多資訊:https://go.microsoft.com/fwlink/?linkid=2257346"
|
||||
de: "Installieren Sie gsudo oder aktivieren Sie Windows Sudo, um diese Schritte auszuführen.\nFür Windows Sudo wird der Standardmodus „In einem neuen Fenster“ nicht unterstützt, da er verhindert, dass Topgrade auf den Abschluss der Befehle wartet. Bitte konfigurieren Sie es stattdessen auf „Inline“-Modus.\nMehr erfahren unter https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
"Windows Sudo was found, but it is set to 'In a new window' mode, which prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more.":
|
||||
en: "Windows Sudo was found, but it is set to 'In a new window' mode, which prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more."
|
||||
lt: "Rastas Windows Sudo, bet jis nustatytas „Naujo lango“ režimu, kuris neleidžia Topgrade laukti, kol bus baigtos komandos. Prašome nustatyti „Inline“ režimą.\nDaugiau sužinokite adresu https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
es: "Se encontró Windows Sudo, pero está configurado en el modo 'En una nueva ventana', lo que impide que Topgrade espere a que los comandos finalicen. Por favor, configúrelo en modo 'Inline'.\nMás información en https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
fr: "Windows Sudo a été trouvé, mais il est configuré en mode « Dans une nouvelle fenêtre », ce qui empêche Topgrade d’attendre la fin des commandes. Veuillez le configurer en mode « Inline ».\nEn savoir plus sur https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
zh_CN: "检测到 Windows Sudo,但其被设置为“新窗口”模式,这会阻止 Topgrade 等待命令完成。请将其配置为“内联”模式。\n了解更多信息:https://go.microsoft.com/fwlink/?linkid=2257346"
|
||||
zh_TW: "偵測到 Windows Sudo,但它被設定為「新視窗」模式,這會阻止 Topgrade 等待命令完成。請將其設定為「內嵌」模式。\n了解更多資訊:https://go.microsoft.com/fwlink/?linkid=2257346"
|
||||
de: "Windows Sudo wurde gefunden, aber es ist auf den Modus „In einem neuen Fenster“ eingestellt, wodurch Topgrade nicht auf den Abschluss der Befehle warten kann. Bitte konfigurieren Sie es stattdessen auf den „Inline“-Modus.\nMehr erfahren unter https://go.microsoft.com/fwlink/?linkid=2257346."
|
||||
"Cannot find sudo binary":
|
||||
en: "Cannot find sudo binary"
|
||||
lt: "Nepavyko rasti sudo dvejetainio failo"
|
||||
es: "No se puede encontrar el binario de sudo"
|
||||
fr: "Impossible de trouver le binaire sudo"
|
||||
zh_CN: "找不到 sudo 可执行文件"
|
||||
zh_TW: "找不到 sudo 可執行檔"
|
||||
de: "Kann die sudo-Binärdatei nicht finden"
|
||||
"Found Windows Sudo, but it is disabled":
|
||||
en: "Found Windows Sudo, but it is disabled"
|
||||
lt: "Rastas Windows Sudo, bet jis išjungtas"
|
||||
es: "Se encontró Windows Sudo, pero está deshabilitado"
|
||||
fr: "Windows Sudo a été trouvé, mais il est désactivé"
|
||||
zh_CN: "检测到 Windows Sudo,但已被禁用"
|
||||
zh_TW: "偵測到 Windows Sudo,但已被停用"
|
||||
de: "Windows Sudo gefunden, aber es ist deaktiviert"
|
||||
"Found Windows Sudo, but it is using 'In a new window' mode":
|
||||
en: "Found Windows Sudo, but it is using 'In a new window' mode"
|
||||
lt: "Rastas Windows Sudo, bet jis naudoja „Naujo lango“ režimą"
|
||||
es: "Se encontró Windows Sudo, pero está usando el modo 'En una nueva ventana'"
|
||||
fr: "Windows Sudo a été trouvé, mais il utilise le mode « Dans une nouvelle fenêtre »"
|
||||
zh_CN: "检测到 Windows Sudo,但其正在使用“新窗口”模式"
|
||||
zh_TW: "偵測到 Windows Sudo,但它正在使用「新視窗」模式"
|
||||
de: "Windows Sudo gefunden, aber es verwendet den Modus „In einem neuen Fenster“"
|
||||
"{sudo_kind} does not support the {option} option":
|
||||
en: "%{sudo_kind} does not support the %{option} option"
|
||||
lt: "%{sudo_kind} nepalaiko parinkties %{option}"
|
||||
|
||||
38
src/main.rs
38
src/main.rs
@@ -28,7 +28,7 @@ use self::error::Upgraded;
|
||||
use self::runner::StepResult;
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use self::steps::{remote::*, *};
|
||||
use self::sudo::{Sudo, SudoKind};
|
||||
use self::sudo::{Sudo, SudoCreateError, SudoKind};
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use self::terminal::*;
|
||||
use self::utils::{install_color_eyre, install_tracing, is_elevated, update_tracing};
|
||||
@@ -155,6 +155,11 @@ fn run() -> Result<()> {
|
||||
};
|
||||
debug!("Sudo: {:?}", sudo);
|
||||
|
||||
let (sudo, sudo_err) = match sudo {
|
||||
Ok(sudo) => (Some(sudo), None),
|
||||
Err(e) => (None, Some(e)),
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let distribution = linux::Distribution::detect();
|
||||
|
||||
@@ -241,13 +246,32 @@ fn run() -> Result<()> {
|
||||
print_warning(t!(
|
||||
"\nSome steps were skipped as sudo or equivalent could not be found."
|
||||
));
|
||||
// Steps can only fail with SkippedMissingSudo if sudo is None,
|
||||
// therefore we must have a sudo_err
|
||||
match sudo_err.unwrap() {
|
||||
SudoCreateError::CannotFindBinary => {
|
||||
#[cfg(unix)]
|
||||
print_warning(t!(
|
||||
"Install one of `sudo`, `doas`, `pkexec`, `run0` or `please` to run these steps."
|
||||
));
|
||||
|
||||
#[cfg(unix)]
|
||||
print_warning(t!(
|
||||
"Install one of `sudo`, `doas`, `pkexec`, `run0` or `please` to run these steps."
|
||||
));
|
||||
#[cfg(windows)]
|
||||
print_warning(t!("Install gsudo or enable Windows Sudo to run these steps."));
|
||||
// if this windows version supported Windows Sudo, the error would have been WinSudoDisabled
|
||||
#[cfg(windows)]
|
||||
print_warning(t!("Install gsudo to run these steps."));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
SudoCreateError::WinSudoDisabled => {
|
||||
print_warning(t!(
|
||||
"Install gsudo or enable Windows Sudo to run these steps.\nFor Windows Sudo, the default 'In a new window' mode is not supported as it prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more."
|
||||
));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
SudoCreateError::WinSudoNewWindowMode => {
|
||||
print_warning(t!(
|
||||
"Windows Sudo was found, but it is set to 'In a new window' mode, which prevents Topgrade from waiting for commands to finish. Please configure it to use 'Inline' mode instead.\nGo to https://go.microsoft.com/fwlink/?linkid=2257346 to learn more."
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
158
src/sudo.rs
158
src/sudo.rs
@@ -2,10 +2,20 @@ use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(windows)]
|
||||
use color_eyre::eyre;
|
||||
#[cfg(windows)]
|
||||
use color_eyre::eyre::eyre;
|
||||
use color_eyre::eyre::Context;
|
||||
use color_eyre::eyre::Result;
|
||||
use rust_i18n::t;
|
||||
use serde::Deserialize;
|
||||
use strum::Display;
|
||||
use thiserror::Error;
|
||||
#[cfg(windows)]
|
||||
use tracing::{debug, warn};
|
||||
#[cfg(windows)]
|
||||
use windows::Win32::Foundation::ERROR_FILE_NOT_FOUND;
|
||||
|
||||
use crate::command::CommandExt;
|
||||
use crate::error::UnsupportedSudo;
|
||||
@@ -22,6 +32,37 @@ pub struct Sudo {
|
||||
kind: SudoKind,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SudoCreateError {
|
||||
CannotFindBinary,
|
||||
#[cfg(windows)]
|
||||
WinSudoDisabled,
|
||||
#[cfg(windows)]
|
||||
WinSudoNewWindowMode,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SudoCreateError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SudoCreateError::CannotFindBinary => {
|
||||
write!(f, "{}", t!("Cannot find sudo binary"))
|
||||
}
|
||||
#[cfg(windows)]
|
||||
SudoCreateError::WinSudoDisabled => {
|
||||
write!(f, "{}", t!("Found Windows Sudo, but it is disabled"))
|
||||
}
|
||||
#[cfg(windows)]
|
||||
SudoCreateError::WinSudoNewWindowMode => {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
t!("Found Windows Sudo, but it is using 'In a new window' mode")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub enum SudoPreserveEnv<'a> {
|
||||
/// Preserve all environment variables.
|
||||
@@ -89,7 +130,7 @@ impl<'a> SudoExecuteOpts<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(not(windows))]
|
||||
const DETECT_ORDER: [SudoKind; 5] = [
|
||||
SudoKind::Doas,
|
||||
SudoKind::Sudo,
|
||||
@@ -98,28 +139,119 @@ const DETECT_ORDER: [SudoKind; 5] = [
|
||||
SudoKind::Please,
|
||||
];
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
// NOTE: keep WinSudo last, allows short-circuit error return in Sudo::detect() to work
|
||||
#[cfg(windows)]
|
||||
const DETECT_ORDER: [SudoKind; 2] = [SudoKind::Gsudo, SudoKind::WinSudo];
|
||||
|
||||
impl Sudo {
|
||||
/// Get the `sudo` binary for this platform.
|
||||
pub fn detect() -> Option<Self> {
|
||||
pub fn detect() -> Result<Self, SudoCreateError> {
|
||||
use SudoCreateError::*;
|
||||
|
||||
for kind in DETECT_ORDER {
|
||||
if let Some(sudo) = Self::new(kind) {
|
||||
return Some(sudo);
|
||||
match Self::new(kind) {
|
||||
Ok(sudo) => return Ok(sudo),
|
||||
Err(CannotFindBinary) => continue,
|
||||
#[cfg(windows)]
|
||||
Err(e @ (WinSudoDisabled | WinSudoNewWindowMode)) => {
|
||||
// we can return directly here since WinSudo is detected last
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
Err(CannotFindBinary)
|
||||
}
|
||||
|
||||
/// Create Sudo from SudoKind, if found in the system
|
||||
pub fn new(kind: SudoKind) -> Option<Self> {
|
||||
match kind {
|
||||
SudoKind::Null => Some(Self {
|
||||
path: None, // no actual binary for null sudo
|
||||
kind,
|
||||
}),
|
||||
_ => kind.which().map(|path| Self { path: Some(path), kind }),
|
||||
pub fn new(kind: SudoKind) -> Result<Self, SudoCreateError> {
|
||||
// no actual binary for null sudo
|
||||
if let SudoKind::Null = kind {
|
||||
return Ok(Self { path: None, kind });
|
||||
}
|
||||
|
||||
match kind.which() {
|
||||
Some(path) => {
|
||||
let sudo = Self { path: Some(path), kind };
|
||||
|
||||
#[cfg(windows)]
|
||||
if let SudoKind::WinSudo = kind {
|
||||
// Windows Sudo might be disabled, causing it to error on use.
|
||||
//
|
||||
// It checks two registry keys to determine its mode:
|
||||
// a "policy" (HLKM\SOFTWARE\Policies\Microsoft\Windows\Sudo\Enabled)
|
||||
// and a "setting" (HLKM\SOFTWARE\Microsoft\Windows\CurrentVersion\Sudo\Enabled).
|
||||
//
|
||||
// Both keys are u32's, with these meanings:
|
||||
// 0 - Disabled
|
||||
// 1 - ForceNewWindow
|
||||
// 2 - DisableInput
|
||||
// 3 - Normal
|
||||
//
|
||||
// Setting the sudo option in Settings changes the setting key, the policy key
|
||||
// sets an upper limit on the setting key: mode = min(policy, setting).
|
||||
// The default for the policy key is 3 (all modes allowed), and the default for
|
||||
// the setting key is 0 (disabled).
|
||||
//
|
||||
// See https://github.com/microsoft/sudo/blob/9f50d79704a9d4d468bc59f725993714762981ca/sudo/src/helpers.rs#L442
|
||||
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
enum SudoMode {
|
||||
Disabled = 0,
|
||||
ForceNewWindow = 1,
|
||||
DisableInput = 2,
|
||||
Normal = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for SudoMode {
|
||||
type Error = eyre::Error;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self> {
|
||||
match value {
|
||||
0 => Ok(SudoMode::Disabled),
|
||||
1 => Ok(SudoMode::ForceNewWindow),
|
||||
2 => Ok(SudoMode::DisableInput),
|
||||
3 => Ok(SudoMode::Normal),
|
||||
_ => Err(eyre!("invalid integer SudoMode: {value}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mode(key: &str, on_missing: SudoMode) -> SudoMode {
|
||||
match windows_registry::LOCAL_MACHINE
|
||||
.open(key)
|
||||
.and_then(|k| k.get_u32("Enabled"))
|
||||
{
|
||||
Ok(v) => v.min(3).try_into().unwrap(),
|
||||
Err(e) if e.code() == ERROR_FILE_NOT_FOUND.to_hresult() => on_missing,
|
||||
Err(e) => {
|
||||
// warn, but treat as normal (using sudo should error)
|
||||
warn!(r"Error reading registry key HKLM\{key}\Enabled: {e}");
|
||||
SudoMode::Normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default to normal if key missing
|
||||
let policy_mode = get_mode(r"SOFTWARE\Policies\Microsoft\Windows\Sudo", SudoMode::Normal);
|
||||
debug!("Windows Sudo policy mode: {policy_mode:?}");
|
||||
// default to disabled if key missing
|
||||
let setting_mode = get_mode(r"SOFTWARE\Microsoft\Windows\CurrentVersion\Sudo", SudoMode::Disabled);
|
||||
debug!("Windows Sudo setting mode: {setting_mode:?}");
|
||||
|
||||
let sudo_mode = policy_mode.min(setting_mode);
|
||||
debug!("Windows Sudo mode: {sudo_mode:?}");
|
||||
|
||||
if sudo_mode == SudoMode::Disabled {
|
||||
return Err(SudoCreateError::WinSudoDisabled);
|
||||
} else if sudo_mode == SudoMode::ForceNewWindow {
|
||||
return Err(SudoCreateError::WinSudoNewWindowMode);
|
||||
}
|
||||
// Normal mode is best, but DisableInput doesn't seem to cause issues
|
||||
}
|
||||
|
||||
Ok(sudo)
|
||||
}
|
||||
None => Err(SudoCreateError::CannotFindBinary),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user