refact: terminal, win, run as admin (#12300)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-07-15 16:32:14 +08:00
committed by GitHub
parent 8d559725d5
commit abb7748ee9
59 changed files with 920 additions and 42 deletions

8
Cargo.lock generated
View File

@@ -2218,9 +2218,8 @@ dependencies = [
[[package]]
name = "filedescriptor"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
version = "0.8.2"
source = "git+https://github.com/rustdesk-org/wezterm?branch=rustdesk/pty_based_0.8.1#80174f8009f41565f0fa8c66dab90d4f9211ae16"
dependencies = [
"libc",
"thiserror 1.0.61",
@@ -5233,8 +5232,7 @@ dependencies = [
[[package]]
name = "portable-pty"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be"
source = "git+https://github.com/rustdesk-org/wezterm?branch=rustdesk/pty_based_0.8.1#80174f8009f41565f0fa8c66dab90d4f9211ae16"
dependencies = [
"anyhow",
"bitflags 1.3.2",

View File

@@ -98,7 +98,7 @@ ctrlc = "3.2"
# arboard = { version = "3.4", features = ["wayland-data-control"] }
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] }
clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" }
portable-pty = "0.8.1" # higher version not work on rustc 1.75
portable-pty = { git = "https://github.com/rustdesk-org/wezterm", branch = "rustdesk/pty_based_0.8.1", package = "portable-pty" }
system_shutdown = "4.0"
qrcode-generator = "4.1"

View File

@@ -2124,6 +2124,10 @@ enum UriLinkType {
terminal,
}
setEnvTerminalAdmin() {
bind.mainSetEnv(key: 'IS_TERMINAL_ADMIN', value: 'Y');
}
// uri link handler
bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
List<String>? args;
@@ -2191,6 +2195,12 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
id = args[i + 1];
i++;
break;
case '--terminal-admin':
setEnvTerminalAdmin();
type = UriLinkType.terminal;
id = args[i + 1];
i++;
break;
case '--password':
password = args[i + 1];
i++;
@@ -2264,7 +2274,8 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
"view-camera",
"port-forward",
"rdp",
"terminal"
"terminal",
"terminal-admin",
];
if (uri.authority.isEmpty &&
uri.path.split('').every((char) => char == '/')) {
@@ -2334,6 +2345,10 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
} else if (command == '--terminal') {
connect(Get.context!, id,
isTerminal: true, forceRelay: forceRelay, password: password);
} else if (command == 'terminal-admin') {
setEnvTerminalAdmin();
connect(Get.context!, id,
isTerminal: true, forceRelay: forceRelay, password: password);
} else {
// Default to remote desktop for '--connect', '--play', or direct connection
connect(Get.context!, id, forceRelay: forceRelay, password: password);

View File

@@ -819,23 +819,33 @@ void enterPasswordDialog(
}
void enterUserLoginDialog(
SessionID sessionId, OverlayDialogManager dialogManager) async {
SessionID sessionId,
OverlayDialogManager dialogManager,
String osAccountDescTip,
bool canRememberAccount) async {
await _connectDialog(
sessionId,
dialogManager,
osUsernameController: TextEditingController(),
osPasswordController: TextEditingController(),
osAccountDescTip: osAccountDescTip,
canRememberAccount: canRememberAccount,
);
}
void enterUserLoginAndPasswordDialog(
SessionID sessionId, OverlayDialogManager dialogManager) async {
SessionID sessionId,
OverlayDialogManager dialogManager,
String osAccountDescTip,
bool canRememberAccount) async {
await _connectDialog(
sessionId,
dialogManager,
osUsernameController: TextEditingController(),
osPasswordController: TextEditingController(),
passwordController: TextEditingController(),
osAccountDescTip: osAccountDescTip,
canRememberAccount: canRememberAccount,
);
}
@@ -845,17 +855,28 @@ _connectDialog(
TextEditingController? osUsernameController,
TextEditingController? osPasswordController,
TextEditingController? passwordController,
String? osAccountDescTip,
bool canRememberAccount = true,
}) async {
final errUsername = ''.obs;
var rememberPassword = false;
if (passwordController != null) {
rememberPassword =
await bind.sessionGetRemember(sessionId: sessionId) ?? false;
}
var rememberAccount = false;
if (osUsernameController != null) {
if (canRememberAccount && osUsernameController != null) {
rememberAccount =
await bind.sessionGetRemember(sessionId: sessionId) ?? false;
}
if (osUsernameController != null) {
osUsernameController.addListener(() {
if (errUsername.value.isNotEmpty) {
errUsername.value = '';
}
});
}
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
cancel() {
@@ -864,6 +885,13 @@ _connectDialog(
}
submit() {
if (osUsernameController != null) {
if (osUsernameController.text.trim().isEmpty) {
errUsername.value = translate('Empty Username');
setState(() {});
return;
}
}
final osUsername = osUsernameController?.text.trim() ?? '';
final osPassword = osPasswordController?.text.trim() ?? '';
final password = passwordController?.text.trim() ?? '';
@@ -927,26 +955,39 @@ _connectDialog(
}
return Column(
children: [
descWidget(translate('login_linux_tip')),
if (osAccountDescTip != null) descWidget(translate(osAccountDescTip)),
DialogTextField(
title: translate(DialogTextField.kUsernameTitle),
controller: osUsernameController,
prefixIcon: DialogTextField.kUsernameIcon,
errorText: null,
),
if (errUsername.value.isNotEmpty)
Align(
alignment: Alignment.centerLeft,
child: SelectableText(
errUsername.value,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 12,
),
textAlign: TextAlign.left,
).paddingOnly(left: 12, bottom: 2),
),
PasswordWidget(
controller: osPasswordController,
autoFocus: false,
),
rememberWidget(
translate('remember_account_tip'),
rememberAccount,
(v) {
if (v != null) {
setState(() => rememberAccount = v);
}
},
),
if (canRememberAccount)
rememberWidget(
translate('remember_account_tip'),
rememberAccount,
(v) {
if (v != null) {
setState(() => rememberAccount = v);
}
},
),
],
);
}

View File

@@ -492,6 +492,7 @@ abstract class BasePeerCard extends StatelessWidget {
bool isTcpTunneling = false,
bool isRDP = false,
bool isTerminal = false,
bool isTerminalRunAsAdmin = false,
}) {
return MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Text(
@@ -499,6 +500,9 @@ abstract class BasePeerCard extends StatelessWidget {
style: style,
),
proc: () {
if (isTerminalRunAsAdmin) {
setEnvTerminalAdmin();
}
connectInPeerTab(
context,
peer,
@@ -507,7 +511,7 @@ abstract class BasePeerCard extends StatelessWidget {
isViewCamera: isViewCamera,
isTcpTunneling: isTcpTunneling,
isRDP: isRDP,
isTerminal: isTerminal,
isTerminal: isTerminal || isTerminalRunAsAdmin,
);
},
padding: menuPadding,
@@ -552,6 +556,15 @@ abstract class BasePeerCard extends StatelessWidget {
);
}
@protected
MenuEntryBase<String> _terminalRunAsAdminAction(BuildContext context) {
return _connectCommonAction(
context,
translate('Terminal (Run as administrator)'),
isTerminalRunAsAdmin: true,
);
}
@protected
MenuEntryBase<String> _tcpTunnelingAction(BuildContext context) {
return _connectCommonAction(
@@ -906,6 +919,10 @@ class RecentPeerCard extends BasePeerCard {
_terminalAction(context),
];
if (peer.platform == kPeerPlatformWindows) {
menuItems.add(_terminalRunAsAdminAction(context));
}
final List favs = (await bind.mainGetFav()).toList();
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
@@ -966,6 +983,11 @@ class FavoritePeerCard extends BasePeerCard {
_viewCameraAction(context),
_terminalAction(context),
];
if (peer.platform == kPeerPlatformWindows) {
menuItems.add(_terminalRunAsAdminAction(context));
}
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context));
}
@@ -1022,6 +1044,10 @@ class DiscoveredPeerCard extends BasePeerCard {
_terminalAction(context),
];
if (peer.platform == kPeerPlatformWindows) {
menuItems.add(_terminalRunAsAdminAction(context));
}
final List favs = (await bind.mainGetFav()).toList();
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
@@ -1076,6 +1102,11 @@ class AddressBookPeerCard extends BasePeerCard {
_viewCameraAction(context),
_terminalAction(context),
];
if (peer.platform == kPeerPlatformWindows) {
menuItems.add(_terminalRunAsAdminAction(context));
}
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context));
}
@@ -1212,6 +1243,11 @@ class MyGroupPeerCard extends BasePeerCard {
_viewCameraAction(context),
_terminalAction(context),
];
if (peer.platform == kPeerPlatformWindows) {
menuItems.add(_terminalRunAsAdminAction(context));
}
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
menuItems.add(_tcpTunnelingAction(context));
}

View File

@@ -230,6 +230,12 @@ class WebHomePage extends StatelessWidget {
id = args[i + 1];
i++;
break;
case '--terminal-admin':
setEnvTerminalAdmin();
isTerminal = true;
id = args[i + 1];
i++;
break;
case '--password':
password = args[i + 1];
i++;

View File

@@ -836,10 +836,16 @@ class FfiModel with ChangeNotifier {
} else if (type == 'input-password') {
enterPasswordDialog(sessionId, dialogManager);
} else if (type == 'session-login' || type == 'session-re-login') {
enterUserLoginDialog(sessionId, dialogManager);
} else if (type == 'session-login-password' ||
type == 'session-login-password') {
enterUserLoginAndPasswordDialog(sessionId, dialogManager);
enterUserLoginDialog(sessionId, dialogManager, 'login_linux_tip', true);
} else if (type == 'session-login-password') {
enterUserLoginAndPasswordDialog(
sessionId, dialogManager, 'login_linux_tip', true);
} else if (type == 'terminal-admin-login') {
enterUserLoginDialog(
sessionId, dialogManager, 'terminal-admin-login-tip', false);
} else if (type == 'terminal-admin-login-password') {
enterUserLoginAndPasswordDialog(
sessionId, dialogManager, 'terminal-admin-login-tip', false);
} else if (type == 'restarting') {
showMsgBox(sessionId, type, title, text, link, false, dialogManager,
hasCancel: false);

View File

@@ -1611,6 +1611,7 @@ struct ConnToken {
pub struct LoginConfigHandler {
id: String,
pub conn_type: ConnType,
pub is_terminal_admin: bool,
hash: Hash,
password: Vec<u8>, // remember password for reconnect
pub remember: bool,
@@ -1736,6 +1737,7 @@ impl LoginConfigHandler {
self.other_server = Some((real_id.to_owned(), server.to_owned(), other_server_key));
}
}
self.direct = None;
self.received = false;
self.switch_uuid = switch_uuid;
@@ -1744,6 +1746,11 @@ impl LoginConfigHandler {
self.shared_password = shared_password;
self.record_state = false;
self.record_permission = true;
// `std::env::remove_var("IS_TERMINAL_ADMIN");` is called in `session_add_sync()` - `flutter_ffi.rs`.
let is_terminal_admin = conn_type == ConnType::TERMINAL
&& std::env::var("IS_TERMINAL_ADMIN").map_or(false, |v| v == "Y");
self.is_terminal_admin = is_terminal_admin;
}
/// Check if the client should auto login.
@@ -1956,7 +1963,7 @@ impl LoginConfigHandler {
.into();
} else if name == keys::OPTION_TERMINAL_PERSISTENT {
config.terminal_persistent.v = !config.terminal_persistent.v;
option.terminal_persistent = (if config.terminal_persistent.v {
option.terminal_persistent = (if config.terminal_persistent.v {
BoolOption::Yes
} else {
BoolOption::No
@@ -3274,6 +3281,19 @@ pub async fn handle_hash(
}
lc.write().unwrap().password = password.clone();
let is_terminal_admin = lc.read().unwrap().is_terminal_admin;
let is_terminal = lc.read().unwrap().conn_type.eq(&ConnType::TERMINAL);
if is_terminal && is_terminal_admin {
if password.is_empty() {
interface.msgbox("terminal-admin-login-password", "", "", "");
} else {
interface.msgbox("terminal-admin-login", "", "", "");
}
lc.write().unwrap().hash = hash;
return;
}
let password = if password.is_empty() {
// login without password, the remote side can click accept
interface.msgbox("input-password", "Password Required", "", "");
@@ -3285,8 +3305,15 @@ pub async fn handle_hash(
hasher.finalize()[..].into()
};
let os_username = lc.read().unwrap().get_option("os-username");
let os_password = lc.read().unwrap().get_option("os-password");
let is_terminal = lc.read().unwrap().conn_type.eq(&ConnType::TERMINAL);
let (os_username, os_password) = if is_terminal {
("".to_owned(), "".to_owned())
} else {
(
lc.read().unwrap().get_option("os-username"),
lc.read().unwrap().get_option("os-password"),
)
};
send_login(lc.clone(), os_username, os_password, password, peer).await;
lc.write().unwrap().hash = hash;

View File

@@ -138,7 +138,7 @@ pub fn session_add_sync(
is_shared_password: bool,
conn_token: Option<String>,
) -> SyncReturn<String> {
if let Err(e) = session_add(
let add_res = session_add(
&session_id,
&id,
is_file_transfer,
@@ -151,7 +151,14 @@ pub fn session_add_sync(
password,
is_shared_password,
conn_token,
) {
);
// We can't put the remove call together with `std::env::var("IS_TERMINAL_ADMIN")`.
// Because there are some `bail!` in `session_add()`, we must make sure `IS_TERMINAL_ADMIN` is removed at last.
if is_terminal {
std::env::remove_var("IS_TERMINAL_ADMIN");
}
if let Err(e) = add_res {
SyncReturn(format!("Failed to add session with id {}, {}", &id, e))
} else {
SyncReturn("".to_owned())
@@ -1067,6 +1074,35 @@ pub fn main_get_env(key: String) -> SyncReturn<String> {
SyncReturn(std::env::var(key).unwrap_or_default())
}
// Dart does not support changing environment variables.
// `Platform.environment['MY_VAR'] = 'VAR';` will throw an error
// `Unsupported operation: Cannot modify unmodifiable map`.
//
// And we need to share the environment variables between rust and dart isolates sometimes.
pub fn main_set_env(key: String, value: Option<String>) -> SyncReturn<()> {
let is_valid_key = !key.is_empty() && !key.contains('=') && !key.contains('\0');
debug_assert!(is_valid_key, "Invalid environment variable key: {}", key);
if !is_valid_key {
log::error!("Invalid environment variable key: {}", key);
return SyncReturn(());
}
match value {
Some(v) => {
let is_valid_value = !v.contains('\0');
debug_assert!(is_valid_value, "Invalid environment variable value: {}", v);
if !is_valid_value {
log::error!("Invalid environment variable value: {}", v);
return SyncReturn(());
}
std::env::set_var(key, v);
}
None => std::env::remove_var(key),
}
SyncReturn(())
}
pub fn main_set_local_option(key: String, value: String) {
let is_texture_render_key = key.eq(config::keys::OPTION_TEXTURE_RENDER);
let is_d3d_render_key = key.eq(config::keys::OPTION_ALLOW_D3D_RENDER);

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "تمكين الطرفية"),
("New tab", "تبويب جديد"),
("Keep terminal sessions on disconnect", "الاحتفاظ بجلسات الطرفية عند قطع الاتصال"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "启用终端"),
("New tab", "新建选项卡"),
("Keep terminal sessions on disconnect", "断开连接时保持终端会话"),
("Terminal (Run as administrator)", "终端(以管理员身份运行)"),
("terminal-admin-login-tip", "请输入被控端的管理员账号密码。"),
("Failed to get user token.", "获取用户令牌时出错。"),
("Incorrect username or password.", "用户名或密码不正确。"),
("The user is not an administrator.", "用户不是管理员。"),
("Failed to check if the user is an administrator.", "检查用户是否为管理员时出错。"),
("Supported only by the installation version.", "仅安装版本支持。"),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Terminal zulassen"),
("New tab", "Neuer Tab"),
("Keep terminal sessions on disconnect", "Terminalsitzungen beim Trennen der Verbindung beibehalten"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -256,5 +256,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "Download failed. You can try again or click the \"Download\" button to download from the release page and upgrade manually."),
("update-failed-check-msi-tip", "Installation method check failed. Please click the \"Download\" button to download from the release page and upgrade manually."),
("websocket_tip", "When using WebSocket, only relay connections are supported."),
("terminal-admin-login-tip", "Please input the administrator username and password of the controlled side."),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "فعال‌سازی ترمینال"),
("New tab", "زبانه جدید"),
("Keep terminal sessions on disconnect", "حفظ جلسات ترمینال پس از قطع اتصال"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Activer le terminal"),
("New tab", "Nouvel onglet"),
("Keep terminal sessions on disconnect", "Maintenir les sessions du terminal lors de la déconnexion"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Terminál engedélyezése"),
("New tab", "Új lap"),
("Keep terminal sessions on disconnect", "Terminál munkamenetek megtartása leválasztáskor"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Abilita terminale"),
("New tab", "Nuova scheda"),
("Keep terminal sessions on disconnect", "Quando disconetti mantieni attiva sessione terminale"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "터미널 사용함"),
("New tab", "새 탭"),
("Keep terminal sessions on disconnect", "터미널 세션 연결 해제 상태 유지"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Iespējot termināli"),
("New tab", "Jauna cilne"),
("Keep terminal sessions on disconnect", "Atvienojoties saglabāt termināļa sesijas"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Terminal inschakelen"),
("New tab", "Nieuw tabblad"),
("Keep terminal sessions on disconnect", "Terminalsessies bij verbreking van de verbinding behouden"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", "Включить терминал"),
("New tab", "Новая вкладка"),
("Keep terminal sessions on disconnect", "Сохранять сеансы терминала при отключении"),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable terminal", ""),
("New tab", ""),
("Keep terminal sessions on disconnect", ""),
("Terminal (Run as administrator)", ""),
("terminal-admin-login-tip", ""),
("Failed to get user token.", ""),
("Incorrect username or password.", ""),
("The user is not an administrator.", ""),
("Failed to check if the user is an administrator.", ""),
("Supported only by the installation version.", ""),
].iter().cloned().collect();
}

View File

@@ -40,7 +40,7 @@ use winapi::{
shared::{minwindef::*, ntdef::NULL, windef::*, winerror::*},
um::{
errhandlingapi::GetLastError,
handleapi::CloseHandle,
handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
libloaderapi::{
GetProcAddress, LoadLibraryA, LoadLibraryExA, LOAD_LIBRARY_SEARCH_SYSTEM32,
},
@@ -49,15 +49,19 @@ use winapi::{
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
OpenProcessToken, ProcessIdToSessionId, PROCESS_INFORMATION, STARTUPINFOW,
},
securitybaseapi::GetTokenInformation,
securitybaseapi::{
AllocateAndInitializeSid, DuplicateToken, EqualSid, FreeSid, GetTokenInformation,
},
shellapi::ShellExecuteW,
sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO},
winbase::*,
wingdi::*,
winnt::{
TokenElevation, ES_AWAYMODE_REQUIRED, ES_CONTINUOUS, ES_DISPLAY_REQUIRED,
SecurityImpersonation, TokenElevation, TokenGroups, TokenImpersonation, TokenType,
DOMAIN_ALIAS_RID_ADMINS, ES_AWAYMODE_REQUIRED, ES_CONTINUOUS, ES_DISPLAY_REQUIRED,
ES_SYSTEM_REQUIRED, HANDLE, PROCESS_ALL_ACCESS, PROCESS_QUERY_LIMITED_INFORMATION,
TOKEN_ELEVATION, TOKEN_QUERY,
PSID, SECURITY_BUILTIN_DOMAIN_RID, SECURITY_NT_AUTHORITY, SID_IDENTIFIER_AUTHORITY,
TOKEN_ELEVATION, TOKEN_GROUPS, TOKEN_QUERY, TOKEN_TYPE,
},
winreg::HKEY_CURRENT_USER,
winspool::{
@@ -521,6 +525,10 @@ extern "C" {
fn is_service_running_w(svc_name: *const u16) -> bool;
}
pub fn get_current_session_id(share_rdp: bool) -> DWORD {
unsafe { get_current_session(if share_rdp { TRUE } else { FALSE }) }
}
extern "system" {
fn BlockInput(v: BOOL) -> BOOL;
}
@@ -2158,6 +2166,177 @@ pub fn send_message_to_hnwd(
return true;
}
pub fn get_logon_user_token(user: &str, pwd: &str) -> ResultType<HANDLE> {
let user_split = user.split("\\").collect::<Vec<&str>>();
let wuser = wide_string(user_split.get(1).unwrap_or(&user));
let wpc = wide_string(user_split.get(0).unwrap_or(&""));
let wpwd = wide_string(pwd);
let mut ph_token: HANDLE = std::ptr::null_mut();
let res = unsafe {
LogonUserW(
wuser.as_ptr(),
wpc.as_ptr(),
wpwd.as_ptr(),
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&mut ph_token as _,
)
};
if res == FALSE {
bail!(
"Failed to log on user {}: {}",
user,
std::io::Error::last_os_error()
);
} else {
if ph_token.is_null() {
bail!(
"Failed to log on user {}: {}",
user,
std::io::Error::last_os_error()
);
}
Ok(ph_token)
}
}
// Ensure the token returned is a primary token.
// If the provided token is an impersonation token, it duplicates it to a primary token.
// If the provided token is already a primary token, it returns it as is.
// The caller is responsible for closing the returned token handle.
pub fn ensure_primary_token(user_token: HANDLE) -> ResultType<HANDLE> {
if user_token.is_null() || user_token == INVALID_HANDLE_VALUE {
bail!("Invalid user token provided");
}
unsafe {
let mut token_type: TOKEN_TYPE = 0;
let mut return_length: DWORD = 0;
if GetTokenInformation(
user_token,
TokenType,
&mut token_type as *mut _ as *mut _,
std::mem::size_of::<TOKEN_TYPE>() as DWORD,
&mut return_length,
) == FALSE
{
bail!(
"Failed to get token type, error {}",
io::Error::last_os_error()
);
}
if token_type == TokenImpersonation {
let mut duplicate_token: HANDLE = std::ptr::null_mut();
let dup_res = DuplicateToken(user_token, SecurityImpersonation, &mut duplicate_token);
CloseHandle(user_token);
if dup_res == FALSE {
bail!(
"Failed to duplicate token, error {}",
io::Error::last_os_error()
);
}
Ok(duplicate_token)
} else {
Ok(user_token)
}
}
}
pub fn is_user_token_admin(user_token: HANDLE) -> ResultType<bool> {
if user_token.is_null() || user_token == INVALID_HANDLE_VALUE {
bail!("Invalid user token provided");
}
unsafe {
let mut dw_size: DWORD = 0;
GetTokenInformation(
user_token,
TokenGroups,
std::ptr::null_mut(),
0,
&mut dw_size,
);
let last_error = GetLastError();
if last_error != ERROR_INSUFFICIENT_BUFFER {
bail!(
"Failed to get token groups buffer size, error: {}",
last_error
);
}
if dw_size == 0 {
bail!("Token groups buffer size is zero");
}
let mut buffer = vec![0u8; dw_size as usize];
if GetTokenInformation(
user_token,
TokenGroups,
buffer.as_mut_ptr() as *mut _,
dw_size,
&mut dw_size,
) == FALSE
{
bail!(
"Failed to get token groups information, error: {}",
io::Error::last_os_error()
);
}
let p_token_groups = buffer.as_ptr() as *const TOKEN_GROUPS;
let group_count = (*p_token_groups).GroupCount;
if group_count == 0 {
return Ok(false);
}
let mut nt_authority: SID_IDENTIFIER_AUTHORITY = SID_IDENTIFIER_AUTHORITY {
Value: SECURITY_NT_AUTHORITY,
};
let mut administrators_group: PSID = std::ptr::null_mut();
if AllocateAndInitializeSid(
&mut nt_authority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&mut administrators_group,
) == FALSE
{
bail!(
"Failed to allocate administrators group SID, error: {}",
io::Error::last_os_error()
);
}
if administrators_group.is_null() {
bail!("Failed to create administrators group SID");
}
let mut is_admin = false;
let groups =
std::slice::from_raw_parts((*p_token_groups).Groups.as_ptr(), group_count as usize);
for group in groups {
if EqualSid(administrators_group, group.Sid) == TRUE {
is_admin = true;
break;
}
}
if !administrators_group.is_null() {
FreeSid(administrators_group);
}
Ok(is_admin)
}
}
pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> ResultType<()> {
let last_error_table = HashMap::from([
(

View File

@@ -56,6 +56,8 @@ use std::{
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use system_shutdown;
#[cfg(target_os = "windows")]
use windows::Win32::Foundation::{CloseHandle, HANDLE};
#[cfg(windows)]
use crate::virtual_display_manager;
@@ -172,6 +174,22 @@ pub enum AuthConnType {
Terminal,
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[derive(Clone, Debug)]
enum TerminalUserToken {
SelfUser,
CurrentLogonUser(crate::terminal_service::UserToken),
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
impl TerminalUserToken {
fn to_terminal_service_token(&self) -> Option<crate::terminal_service::UserToken> {
match self {
TerminalUserToken::SelfUser => None,
TerminalUserToken::CurrentLogonUser(token) => Some(*token),
}
}
}
pub struct Connection {
inner: ConnInner,
display_idx: usize,
@@ -254,6 +272,11 @@ pub struct Connection {
tx_post_seq: mpsc::UnboundedSender<(String, Value)>,
terminal_service_id: String,
terminal_persistent: bool,
// The user token must be set when terminal is enabled.
// 0 indicates SYSTEM user
// other values indicate current user
#[cfg(not(any(target_os = "android", target_os = "ios")))]
terminal_user_token: Option<TerminalUserToken>,
terminal_generic_service: Option<Box<GenericService>>,
}
@@ -418,6 +441,8 @@ impl Connection {
tx_post_seq,
terminal_service_id: "".to_owned(),
terminal_persistent: false,
#[cfg(not(any(target_os = "android", target_os = "ios")))]
terminal_user_token: None,
terminal_generic_service: None,
};
let addr = hbb_common::try_into_v4(addr);
@@ -1415,12 +1440,19 @@ impl Connection {
.unwrap()
.insert(self.lr.my_id.clone(), self.tx_input.clone());
// Terminal feature is supported on desktop only
#[allow(unused_mut)]
let mut terminal = cfg!(not(any(target_os = "android", target_os = "ios")));
#[cfg(target_os = "windows")]
{
terminal = terminal && portable_pty::win::check_support().is_ok();
}
pi.username = username;
pi.sas_enabled = sas_enabled;
pi.features = Some(Features {
privacy_mode: privacy_mode::is_privacy_mode_supported(),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
terminal: true, // Terminal feature is supported on desktop only
terminal,
..Default::default()
})
.into();
@@ -1429,7 +1461,9 @@ impl Connection {
#[allow(unused_mut)]
let mut wait_session_id_confirm = false;
#[cfg(windows)]
self.handle_windows_specific_session(&mut pi, &mut wait_session_id_confirm);
if !self.terminal {
self.handle_windows_specific_session(&mut pi, &mut wait_session_id_confirm);
}
if self.file_transfer.is_some() || self.terminal {
res.set_peer_info(pi);
} else if self.view_camera {
@@ -1933,12 +1967,28 @@ impl Connection {
sleep(1.).await;
return false;
}
#[cfg(target_os = "windows")]
if !lr.os_login.username.is_empty() && !crate::platform::is_installed() {
self.send_login_error("Supported only by the installation version.")
.await;
sleep(1.).await;
return false;
}
self.terminal = true;
if let Some(o) = self.options_in_login.as_ref() {
self.terminal_persistent =
o.terminal_persistent.enum_value() == Ok(BoolOption::Yes);
}
self.terminal_service_id = terminal.service_id;
#[cfg(target_os = "windows")]
if let Some(msg) =
self.fill_terminal_user_token(&lr.os_login.username, &lr.os_login.password)
{
self.send_login_error(msg).await;
sleep(1.).await;
return false;
}
}
Some(login_request::Union::PortForward(mut pf)) => {
if !Connection::permission("enable-tunnel") {
@@ -2893,6 +2943,94 @@ impl Connection {
true
}
// Try to fill user token for terminal connection.
// If username is empty, use the user token of the current session.
// If username is not empty, try to logon and check if the user is an administrator.
// If the user is an administrator, use the user token of current process (SYSTEM).
// If the user is not an administrator, return an error message.
// Note: Only local and domain users are supported, Microsoft account (online account) not supported for now.
#[cfg(target_os = "windows")]
fn fill_terminal_user_token(&mut self, username: &str, password: &str) -> Option<&'static str> {
// No need to check if the password is empty.
if !username.is_empty() {
return self.handle_administrator_check(username, password);
}
if crate::platform::is_prelogin() {
self.terminal_user_token = None;
return Some("No active console user logged on, please connect and logon first.");
}
if crate::platform::is_installed() {
return self.handle_installed_user();
}
self.terminal_user_token = Some(TerminalUserToken::SelfUser);
None
}
#[cfg(target_os = "windows")]
fn handle_administrator_check(
&mut self,
username: &str,
password: &str,
) -> Option<&'static str> {
let check_admin_res =
crate::platform::get_logon_user_token(username, password).map(|token| {
let is_token_admin = crate::platform::is_user_token_admin(token);
unsafe {
hbb_common::allow_err!(CloseHandle(HANDLE(token as _)));
};
is_token_admin
});
match check_admin_res {
Ok(Ok(b)) => {
if b {
self.terminal_user_token = Some(TerminalUserToken::SelfUser);
None
} else {
Some("The user is not an administrator.")
}
}
Ok(Err(e)) => {
log::error!("Failed to check if the user is an administrator: {}", e);
Some("Failed to check if the user is an administrator.")
}
Err(e) => {
log::error!("Failed to get logon user token: {}", e);
Some("Incorrect username or password.")
}
}
}
#[cfg(target_os = "windows")]
fn handle_installed_user(&mut self) -> Option<&'static str> {
let session_id = crate::platform::get_current_session_id(true);
if session_id == 0xFFFFFFFF {
return Some("Failed to get current session id.");
}
let token = crate::platform::get_user_token(session_id, true);
if !token.is_null() {
match crate::platform::ensure_primary_token(token) {
Ok(t) => {
self.terminal_user_token = Some(TerminalUserToken::CurrentLogonUser(t as _));
}
Err(e) => {
log::error!("Failed to ensure primary token: {}", e);
self.terminal_user_token =
Some(TerminalUserToken::CurrentLogonUser(token as _));
}
}
None
} else {
log::error!(
"Failed to get user token for terminal action, {}",
std::io::Error::last_os_error()
);
Some("Failed to get user token.")
}
}
fn update_failure(&self, (mut failure, time): ((i32, i32, i32), i32), remove: bool, i: usize) {
if remove {
if failure.0 != 0 {
@@ -3833,12 +3971,19 @@ impl Connection {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
async fn init_terminal_service(&mut self) {
debug_assert!(self.terminal_user_token.is_some());
let Some(user_token) = self.terminal_user_token.clone() else {
// unreachable, but keep it for safety
log::error!("Terminal user token is not set.");
return;
};
if self.terminal_service_id.is_empty() {
self.terminal_service_id = terminal_service::generate_service_id();
}
let s = Box::new(terminal_service::new(
self.terminal_service_id.clone(),
self.terminal_persistent,
user_token.to_terminal_service_token(),
));
s.on_subscribe(self.inner.clone());
self.terminal_generic_service = Some(s);
@@ -3846,9 +3991,15 @@ impl Connection {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
async fn handle_terminal_action(&mut self, action: TerminalAction) -> ResultType<()> {
debug_assert!(self.terminal_user_token.is_some());
let Some(user_token) = self.terminal_user_token.clone() else {
// unreacheable, but keep it for safety
bail!("Terminal user token is not set.");
};
let mut proxy = terminal_service::TerminalServiceProxy::new(
self.terminal_service_id.clone(),
Some(self.terminal_persistent),
user_token.to_terminal_service_token(),
);
match proxy.handle_action(&action) {
@@ -4249,6 +4400,15 @@ impl Drop for Connection {
if let Some(s) = self.terminal_generic_service.as_ref() {
s.join();
}
#[cfg(target_os = "windows")]
if let Some(TerminalUserToken::CurrentLogonUser(token)) = self.terminal_user_token.take() {
if token != 0 {
unsafe {
hbb_common::allow_err!(CloseHandle(HANDLE(token as _)));
};
}
}
}
}

View File

@@ -7,6 +7,7 @@ use portable_pty::{Child, CommandBuilder, PtySize};
use std::{
collections::{HashMap, VecDeque},
io::{Read, Write},
ops::{Deref, DerefMut},
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{self, Receiver, SyncSender},
@@ -271,17 +272,51 @@ pub fn get_terminal_session_count(include_zombie_tasks: bool) -> usize {
c
}
pub fn new(service_id: String, is_persistent: bool) -> GenericService {
pub type UserToken = u64;
#[derive(Clone)]
pub struct TerminalService {
sp: GenericService,
user_token: Option<UserToken>,
}
impl Deref for TerminalService {
type Target = ServiceTmpl<ConnInner>;
fn deref(&self) -> &Self::Target {
&self.sp
}
}
impl DerefMut for TerminalService {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sp
}
}
pub fn get_service_name(source: VideoSource, idx: usize) -> String {
format!("{}{}", source.service_name_prefix(), idx)
}
pub fn new(
service_id: String,
is_persistent: bool,
user_token: Option<UserToken>,
) -> GenericService {
// Create the service with initial persistence setting
allow_err!(get_or_create_service(service_id.clone(), is_persistent));
let svc = EmptyExtraFieldService::new(service_id.clone(), false);
let svc = TerminalService {
sp: GenericService::new(service_id.clone(), false),
user_token,
};
GenericService::run(&svc.clone(), move |sp| run(sp, service_id.clone()));
svc.sp
}
fn run(sp: EmptyExtraFieldService, service_id: String) -> ResultType<()> {
fn run(sp: TerminalService, service_id: String) -> ResultType<()> {
while sp.ok() {
let responses = TerminalServiceProxy::new(service_id.clone(), None).read_outputs();
let responses = TerminalServiceProxy::new(service_id.clone(), None, sp.user_token.clone())
.read_outputs();
for response in responses {
let mut msg_out = Message::new();
msg_out.set_terminal_response(response);
@@ -451,6 +486,7 @@ impl TerminalSession {
}
drop(input_tx);
}
self.output_rx = None;
// Wait for threads to finish
// The reader thread should join before the writer thread on Windows.
@@ -544,6 +580,8 @@ impl PersistentTerminalService {
pub struct TerminalServiceProxy {
service_id: String,
is_persistent: bool,
#[cfg(target_os = "windows")]
user_token: Option<UserToken>,
}
pub fn set_persistent(service_id: &str, is_persistent: bool) -> Result<()> {
@@ -556,7 +594,11 @@ pub fn set_persistent(service_id: &str, is_persistent: bool) -> Result<()> {
}
impl TerminalServiceProxy {
pub fn new(service_id: String, is_persistent: Option<bool>) -> Self {
pub fn new(
service_id: String,
is_persistent: Option<bool>,
_user_token: Option<UserToken>,
) -> Self {
// Get persistence from the service if it exists
let is_persistent =
is_persistent.unwrap_or(if let Some(service) = get_service(&service_id) {
@@ -567,6 +609,8 @@ impl TerminalServiceProxy {
TerminalServiceProxy {
service_id,
is_persistent,
#[cfg(target_os = "windows")]
user_token: _user_token,
}
}
@@ -670,7 +714,14 @@ impl TerminalServiceProxy {
// Use default shell for the platform
let shell = get_default_shell();
log::debug!("Using shell: {}", shell);
let cmd = CommandBuilder::new(&shell);
#[allow(unused_mut)]
let mut cmd = CommandBuilder::new(&shell);
#[cfg(target_os = "windows")]
if let Some(token) = &self.user_token {
cmd.set_user_token(*token as _);
}
log::debug!("Spawning shell process...");
let child = pty_pair