From d027f524ce374f134e1b49e604856eb175a7de19 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 15 Aug 2022 20:51:11 +0530 Subject: [PATCH] Make allow_remote_control a little more fine grained Also, only respect listen_on if there is a possiblility of rc commands over it being accepted --- kitty/boss.py | 18 +++++++++---- kitty/cli.py | 23 +++++++++-------- kitty/options/definition.py | 51 ++++++++++++++++++++++++------------- kitty/options/parse.py | 31 ++++++++++++---------- kitty/options/types.py | 4 ++- 5 files changed, 79 insertions(+), 48 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 1ce94c490..bde0b07c0 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -258,7 +258,11 @@ class Boss: # we dont allow reloading the config file to change # allow_remote_control self.allow_remote_control = opts.allow_remote_control - if args.listen_on: + if self.allow_remote_control in ('y', 'yes', 'true'): + self.allow_remote_control = 'y' + elif self.allow_remote_control in ('n', 'no', 'false'): + self.allow_remote_control = 'n' + if args.listen_on and self.allow_remote_control in ('y', 'socket', 'socket-only', 'password'): listen_fd = listen_on(args.listen_on) self.prewarm = prewarm self.child_monitor = ChildMonitor( @@ -447,6 +451,10 @@ class Boss: from .remote_control import is_cmd_allowed, parse_cmd response = None window = window or None + if self.allow_remote_control == 'n': + return {'ok': False, 'error': 'Remote control is disabled'} + if self.allow_remote_control == 'socket-only' and peer_id == 0: + return {'ok': False, 'error': 'Remote control is allowed over a socket only'} try: pcmd = parse_cmd(cmd, self.encryption_key) except Exception as e: @@ -454,10 +462,10 @@ class Boss: return response if not pcmd: return response - allowed_by_channel = ( - self.allow_remote_control == 'y' or (peer_id > 0 and self.allow_remote_control == 'socket-only') or - getattr(window, 'allow_remote_control', False)) - if allowed_by_channel: + allowed_unconditionally = ( + self.allow_remote_control == 'y' or (peer_id > 0 and self.allow_remote_control in ('socket-only', 'socket')) or + (window and window.allow_remote_control)) + if allowed_unconditionally: return self._execute_remote_command(pcmd, window, peer_id) q = is_cmd_allowed(pcmd, window, peer_id > 0, {}) if q is True: diff --git a/kitty/cli.py b/kitty/cli.py index 8b140111f..15cff945f 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -677,17 +677,18 @@ regardless of this option. --listen-on Listen on the specified socket address for control messages. For example, -:option:`{appname} --listen-on`=unix:/tmp/mykitty or -:option:`{appname} --listen-on`=tcp:localhost:12345. On Linux systems, you can -also use abstract UNIX sockets, not associated with a file, like this: -:option:`{appname} --listen-on`=unix:@mykitty. Environment variables are -expanded and relative paths are resolved with respect to the temporary -directory. To control kitty, you can send commands to it with -:italic:`{appname} @` using the :option:`{appname} @ --to` option to specify -this address. Note that if you run :italic:`{appname} @` within a kitty window, there is -no need to specify the :option:`{appname} @ --to` option as it will -automatically read from the environment. For UNIX sockets, this can also be -specified in :file:`{conf_name}.conf`. +:option:`{appname} --listen-on`=unix:/tmp/mykitty or :option:`{appname} +--listen-on`=tcp:localhost:12345. On Linux systems, you can also use abstract +UNIX sockets, not associated with a file, like this: :option:`{appname} +--listen-on`=unix:@mykitty. Environment variables are expanded and relative +paths are resolved with respect to the temporary directory. To control kitty, +you can send commands to it with :italic:`{appname} @` using the +:option:`{appname} @ --to` option to specify this address. Note that if you run +:italic:`{appname} @` within a kitty window, there is no need to specify the +:option:`{appname} @ --to` option as it will automatically read from the +environment. Note that this will be ignored unless :opt:`allow_remote_control` +is set to either: :code:`yes`, :code:`socket` or :code:`socket-only`. For UNIX +sockets, this can also be specified in :file:`{conf_name}.conf`. --start-as diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 867c430cb..f0d794873 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -2713,35 +2713,50 @@ that is used to check every remote control command. See :ref:`rc_custom_auth` fo Relative paths are resolved from the kitty configuration directory. ''') -opt('allow_remote_control', 'no', - option_type='allow_remote_control', +opt('allow_remote_control', 'password', choices=('password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true'), long_text=''' Allow other programs to control kitty. If you turn this on, other programs can control all aspects of kitty, including sending text to kitty windows, opening new windows, closing windows, reading the content of windows, etc. Note that -this even works over SSH connections. You can choose to either allow any program -running within kitty to control it with :code:`yes`, or only allow programs that -connect to the socket (specified with the :opt:`listen_on` config option or -:option:`kitty --listen-on` command line option) with the value -:code:`socket-only`. The latter is useful if you want to prevent programs -running on a remote computer over SSH from controlling kitty. Reloading the -config will not affect this option. +this even works over SSH connections. The default setting of :code:`password` +asks the user for confirmation when a remote control command is received. +The meaning of the various values are: + +:code:`password` + Remote control requests received over both the TTY device and the socket are + confirmed based on passwords, see :opt:`remote_control_password`. + +:code:`socket-only` + Remote control requests received over a socket are accepted unconditionally. + Requests received over the TTY are denied. See :opt:`listen_on`. + +:code:`socket` + Remote control requests received over a socket are accepted unconditionally. + Requests received over the TTY are confirmed based on password. + +:code:`no` + Remote control is completely disabled. + +:code:`yes` + Remote control requests are always accepted. + ''' ) opt('listen_on', 'none', long_text=''' -Listen to the specified UNIX socket for remote control connections. Note -that this will apply to all kitty instances. It can be overridden by the -:option:`kitty --listen-on` command line option, which supports listening on TCP -socket. This option accepts only UNIX sockets, such as +Listen to the specified UNIX socket for remote control connections. Note that +this will apply to all kitty instances. It can be overridden by the +:option:`kitty --listen-on` command line option, which also supports listening +on a TCP socket. This option accepts only UNIX sockets, such as :code:`unix:${TEMP}/mykitty` or :code:`unix:@mykitty` (on Linux). Environment variables are expanded and relative paths are resolved with respect to the -temporary directory. If :code:`{kitty_pid}` is present, then it is replaced -by the PID of the kitty process, otherwise the PID of the kitty process is -appended to the value, with a hyphen. See the help for -:option:`kitty --listen-on` for more details. Changing this option by reloading -the config is not supported. +temporary directory. If :code:`{kitty_pid}` is present, then it is replaced by +the PID of the kitty process, otherwise the PID of the kitty process is +appended to the value, with a hyphen. See the help for :option:`kitty +--listen-on` for more details. Note that this will be ignored unless :opt:`allow_remote_control` +is set to either: :code:`yes`, :code:`socket` or :code:`socket-only`. +Changing this option by reloading the config is not supported. ''' ) diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 24b0690e3..469fe9215 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -6,18 +6,18 @@ from kitty.conf.utils import ( unit_float ) from kitty.options.utils import ( - action_alias, active_tab_title_template, allow_hyperlinks, allow_remote_control, bell_on_tab, - box_drawing_scale, clear_all_mouse_actions, clear_all_shortcuts, clipboard_control, - clone_source_strategies, config_or_absolute_path, copy_on_select, cursor_text_color, - deprecated_adjust_line_height, deprecated_hide_window_decorations_aliases, - deprecated_macos_show_window_title_in_menubar_alias, deprecated_send_text, disable_ligatures, - edge_width, env, font_features, hide_window_decorations, macos_option_as_alt, macos_titlebar_color, - modify_font, narrow_symbols, optional_edge_width, parse_map, parse_mouse_map, paste_actions, - remote_control_password, resize_draw_strategy, scrollback_lines, scrollback_pager_history_size, - shell_integration, store_multiple, symbol_map, tab_activity_symbol, tab_bar_edge, - tab_bar_margin_height, tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator, - tab_title_template, titlebar_color, to_cursor_shape, to_font_size, to_layout_names, to_modifiers, - url_prefixes, url_style, visual_window_select_characters, window_border_width, window_size + action_alias, active_tab_title_template, allow_hyperlinks, bell_on_tab, box_drawing_scale, + clear_all_mouse_actions, clear_all_shortcuts, clipboard_control, clone_source_strategies, + config_or_absolute_path, copy_on_select, cursor_text_color, deprecated_adjust_line_height, + deprecated_hide_window_decorations_aliases, deprecated_macos_show_window_title_in_menubar_alias, + deprecated_send_text, disable_ligatures, edge_width, env, font_features, hide_window_decorations, + macos_option_as_alt, macos_titlebar_color, modify_font, narrow_symbols, optional_edge_width, + parse_map, parse_mouse_map, paste_actions, remote_control_password, resize_draw_strategy, + scrollback_lines, scrollback_pager_history_size, shell_integration, store_multiple, symbol_map, + tab_activity_symbol, tab_bar_edge, tab_bar_margin_height, tab_bar_min_tabs, tab_fade, + tab_font_style, tab_separator, tab_title_template, titlebar_color, to_cursor_shape, to_font_size, + to_layout_names, to_modifiers, url_prefixes, url_style, visual_window_select_characters, + window_border_width, window_size ) @@ -54,7 +54,12 @@ class Parser: ans['allow_hyperlinks'] = allow_hyperlinks(val) def allow_remote_control(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['allow_remote_control'] = allow_remote_control(val) + val = val.lower() + if val not in self.choices_for_allow_remote_control: + raise ValueError(f"The value {val} is not a valid choice for allow_remote_control") + ans["allow_remote_control"] = val + + choices_for_allow_remote_control = frozenset(('password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true')) def background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['background'] = to_color(val) diff --git a/kitty/options/types.py b/kitty/options/types.py index d60eb3398..73c575ccb 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -14,6 +14,7 @@ import kitty.types if typing.TYPE_CHECKING: choices_for_allow_cloning = typing.Literal['yes', 'y', 'true', 'no', 'n', 'false', 'ask'] + choices_for_allow_remote_control = typing.Literal['password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true'] choices_for_background_image_layout = typing.Literal['mirror-tiled', 'scaled', 'tiled', 'clamped'] choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'hand'] choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11'] @@ -30,6 +31,7 @@ if typing.TYPE_CHECKING: choices_for_window_logo_position = typing.Literal['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'] else: choices_for_allow_cloning = str + choices_for_allow_remote_control = str choices_for_background_image_layout = str choices_for_default_pointer_shape = str choices_for_linux_display_server = str @@ -467,7 +469,7 @@ class Options: active_tab_title_template: typing.Optional[str] = None allow_cloning: choices_for_allow_cloning = 'ask' allow_hyperlinks: int = 1 - allow_remote_control: str = 'n' + allow_remote_control: choices_for_allow_remote_control = 'password' background: Color = Color(0, 0, 0) background_image: typing.Optional[str] = None background_image_layout: choices_for_background_image_layout = 'tiled'