Classic confinement request for fastHistory

Hi,

I’d like to request classic confinement for my snap fastHistory.
fastHistory is a python tool integrated with the terminal to store important commands, search them and automatically paste them

This is my GitHub project page:
GitHub: https://github.com/mkcn/fastHistory
GitHub snap branch with the yaml file: https://github.com/mkcn/fastHistory/tree/snap/pkg/snap

Classic confinement:
I would like to request the --classic confinement because the core of this program needs to:

  • read and write ~/.bashrc
  • read and write ~/.zshrc
  • inject commands into the terminal stream without execute them, to emulate the “auto-paste” functionality (see the paste_into_terminal function from /fastHistory/console/consoleUtils.py for more info)
  • copy strings to the clipboard (see the copy_to_clipboard function from /fastHistory/console/consoleUtils.py for more info)

It looks like there is no way to have all these functionalities with snap in strict mode, but if there was I would be glad to try again.

Thanks,
Mirko

I don’t think classic confinement should be required - to read/write dot-files there is the personal-files interface - to copy to the clipboard, it looks like you will need to ship whatever binary is required by the snap (xclip etc) within the snap and then plug the x11 interface it should work.

Can you please give these a try and switch the snap to strict confinement?

Thanks for the hint, the personal-files interface solved the first 2 points.
Unfortunately, the x11 interface did not solve the "auto-paste” problem.
From snappy-debug it looks related to the syscall ioctl (and not to a missing interface).

When the “paste_into_terminal” function of my tool is called with strict OR devmode confinement:

	def paste_into_terminal(data):
		"""
		Fill terminal input with data
		# https://unix.stackexchange.com/a/217390
		"""
		try:
			# check if python version >= 3
			if sys.version_info >= (3,):
				# reverse the automatic encoding and pack into a list of bytes
				data_bytes = (struct.pack('B', c) for c in os.fsencode(data))

			# put each char of data in the standard input of the current terminal
			for c in data_bytes:
				fcntl.ioctl(sys.stdin, termios.TIOCSTI, c)
			# clear output printed by the previous command
			# and leave only the terminal with the submitted input
			sys.stdout.write('\r')
			return [True, None]
		except Exception as e:
			return [False, "your terminal does not support auto-paste: " % e] 

…I get this exception:

"your terminal does not support auto-paste: [Errno 1] Operation not permitted"

Meanwhile snappy-debug prints just this:

mkcn@m-dev:~$ sudo snappy-debug
INFO: Following '/var/log/syslog'. If have dropped messages, use:
INFO: $ sudo journalctl --output=short --follow --all | sudo snappy-debug
kernel.printk_ratelimit = 0
= Seccomp =
Time: Apr  2 01:36:23
Log: auid=1000 uid=1000 gid=1001 ses=1 subj=snap.fasthistory.fasthistory pid=29877 comm="python3" exe="/snap/fasthistory/x6/usr/bin/python3.6" sig=0 arch=c000003e 16(ioctl) compat=0 ip=0x7ff8de42f317 code=0x50000
Syscall: ioctl

Snap info:

mkcn@m-dev:~$ snap --version
snap    2.49.1
snapd   2.49.1
series  16
ubuntu  20.04
kernel  5.8.0-48-generic

Is there some sort of limitation about the syscall or is there something else I can try?

Unfortunately the ioctl syscall using TIOCSTI is not allowed for strictly confined snaps - https://github.com/snapcore/snapd/blob/8e45dfe348ef0392c6524e62e63be67a28fe0672/interfaces/seccomp/template.go#L194 - as this could allow them to escape confinement (ie and execute arbitrary commands by injecting these into the terminal) - as such I don’t think there is a way for a snap of fastHistory to support this paste functionality. Is this strictly required? Could the user just manually paste this from the clipboard themselves?

oh I see, using TIOCSTI is indeed a very specific request but it is really a core feature of my program and it is what makes it different from others. The all idea is to reduce the user typing at minimum and I would prefer to use the classic confinement then. Would be ok for you?

Furthermore, I realized that with the confined mode there also other minor features which likely cannot work properly:

  • load_man_page(self, cmd): to retrieve the meaning of command flags (also offline) it needs to execute man {{dynamic-bash-command}} in the user context because the man page of a given command is available only if that command is installed in the OS

    • Screenshot from 2021-04-14 01-35-37
  • [future feature] is_cmd_available_on_this_machine(cmd_name): it uses the which command to show if a command is already installed and available on the user machine

Hi @alexmurray,

is there any update on this?

PS: I am going to release a major update in the coming weeks, should I upload this ASAP to snap for your review or it is best to first wait for your approval / feedbacks of the current uploaded one?

Best,
Mirko

Unfortunately I do not think this snap fits within any of the existing known categories for classic confinement as outlined in Process for reviewing classic confinement snaps - as such whilst I understand it is not able to function fully without classic confinement at this stage, an exception is not able to be granted unless an additional category for classic confinement is able to be established that fasthistory would fit within - @pedronis can you comment on this?

Regarding man pages - I can imagine this may be possible to do within a strictly confined snap if the snap were to include the man binary within itself and use the system-files interface to get read access to /usr/share/man on the host.

What’s the user interaction like that ends up invoking paste_into_terminal ?

I’m not quite sure what the new category would be, it’s also unclear the extent to which the audience for this would be aware of the security trade-offs involved.

@mkcn ping, could you please provide the requested information?

Hi @pedronis, the paste_into_terminal function is invoked only when the user select a command. The selected command is written into the standard input of the terminal but never executed directly. This main goal is to minimized the user interaction and make the command retrieval as fast as possible without any annoying select, copy or paste operation.

From the user prospective:

  • open fastHistory
  • search a command from the list (by scrolling or filter by keywords)
  • press “enter”
  • the command is now in the terminal ready to be modified or executed by the user

Example: https://github.com/mkcn/fastHistory/raw/master/images/advanced_search.gif

About the users’ security awareness, as soon as you give permission to write into your ~/.bashrc (which fastHistory requires) you need to trust the software from my point of view and I would agree to make this very clear in the snap description.

About the category, this tool is deeply connected with the terminal and it works similarly to zsh (for more info see the ‘preexec’ and ‘precmd’ functions). Therefore it may be classified as terminal emulator/shells.

Hmm so I’m not sure why I didn’t raise it earlier but granting the ability to write to ~/.bashrc / ~/.zshrc allows a snap to escape confinement so this would not be approved for use by a strictly confined snap without at least vetting the publisher as we do for classic confinement requests. However, to have a snap which can escape confinement so easily as strictly confined would perhaps give end users a false sense of security - and I think in that case, the use of classic confinement may be more appropriate.

Can you elaborate on the need to write to these configuration files? Would it be possible to instead just have read access to these files and prompt users to make whatever modifications are required themselves (assuming these don’t in themselves allow to escape from the snap confinement)?

Finally, I still think this snap would be quite useful even without the ability to inject the commands into the terminal directly - as they can be copied into the clipboard the user simply needs to Ctrl-Shift-V (or whatever appropriate paste keyboard shortcut is for their terminal emulator) and so I don’t think you should be concerned about not being able to operate as a classic snap - but that is just my opinion - I don’t see this snap as a terminal emulator itself and I can’t easily see from the link above how the use of a few bash functions would make it so so if you could elaborate on that point that would be helpful (if you wish to try and have it supported as classic under that category).

However, to have a snap which can escape confinement so easily as strictly confined would perhaps give end users a false sense of security - and I think in that case, the use of classic confinement may be more appropriate.

My initial request for classic confinement was mainly because this consideration. Allow that with the personal-files interface was a bit curious for me.

To answer your question you need to understand a bit more of the code flow:

  • during installation the following line is added to the $HOME/.bashrc or $HOME/.zshrc which loads the f.sh file.

    source "/home/USER/.local/lib/pythonX.Y/site-packages/fastHistory/bash/f.sh"
    
  • when a terminal is open , f.sh loads bash-preexec.sh and, using the preexec() and precmd() functions, fastHistory (similarly to zsh) can now intercept any user command before and after execution (just the command, not the output).

  • thanks to this trick, any command which ends with a “#” will be stored in fastHistory’s local db. The result is:

    Screenshot from 2021-05-20 19-48-01

Would it be possible to instead just have read access to these files and prompt users to make whatever modifications are required themselves

theoretically yes, this is changed automatically only during installation and version update, but in my opinion it is not the point because as soon as you source my script I could always put malicious stuff in it. If you trust this, the security gain of the snap confinement concept seems very limited.

I don’t see this snap as a terminal emulator itself

I agree, the terminal emulator/shells is not correct, it was just the closest one. If you can create a new category, it may be something like terminal plugin, terminal extension or (more generically) productivity tool.

About the command injection topic, I understand this single change may be ok in exchange for a better security I am just not convinced taking into account all factors.

What do you think?

Snaps are installed globally so not sure how that part of the installation would work. This seems a tool that a user would install in their home directory. Snap might not be a great packaging format for a tool that needs to manipulate those files on installation.

My bad, in the previous message with “installation” I meant the initial configuration when you open it the first time.
Currently, at each run, fastHistory does some basic checks (does the DB for the current user exists? has the current shell correctly loaded the f.sh script?). if something is missing (usually only after an installation or update), fastHistory automatically configures the environment for the current user.

The code of fastHistory can be anywhere, the previous example was a local pip installation but with a global pip installation the result is:

source "/usr/local/lib/python3.8/dist-packages/fastHistory/bash/f.sh"

So, if I have understood your concern correctly, that is not a problem.

Unfortunately as noted earlier, fastHistory does not seem to be a good fit for snap packaging, particularly given the current Process for reviewing classic confinement snaps. As such, this request for classic confinement for fastHistory will not be granted at this time.

ok, thanks for your feedback.