5

I'm writing an application that maintains an encrypted database containing extremely sensitive information including cryptographic keys and passwords. The application is paranoid about using memlock, blocking ptrace, preventing coredumps, etc., but has a command to let the database owner edit the contents of the database. This command writes the entire database to a temporary file in human-readable ASCII format and lets the user edit it. Specifically, it creates a new file in a newly created directory /tmp/XXXXXXXX/ (where /tmp is ideally a memory file system), then runs the user's preferred editor on the file. When the editor exits, the application parses the file contents and shreds the file and anything else under /tmp/XXXXXXXX/ before finally deleting the directory.

Unfortunately, this strategy doesn't work very well with any of the vi variants, because the editor ends up writing copies of the file to /var/tmp/vi.recover, where they will likely persist on disk even after vi deletes them. (The data contains high-value private keys, for which it would be easy to search an entire raw partition.) I'm looking for a generic way to suppress these recovery files, or at the very least move them to /tmp/XXXXXXXX/.

My ideal solution would work for most vi variants, but I'm also happy parsing the EDITOR environment variable and doing something different for each editor. Hence, if you have a solution that works for vim but not the others, that's at least a good start. (I already special-case vim to re-open the editor to the exact column number of a parse error, whereas other vi variants can only be opened to the appropriate line.)

Because this is an application to be distributed to other people, the one thing I cannot do is solve the problem by editing people's .exrc or .vimrc files. In an absolute pinch, I suppose I could change the HOME environment variable before exec'ing the editor and create a temporary .vimrc that merges the user's real one with some recovery-suppression commands. However, I would far prefer a command-line or comment solution.

The closest I can come to a minimal working example is to share what I do for emacs, which is to append the following comment at the end of the file:

# Local Variables:
# make-backup-files: nil
# auto-save-default: nil
# End:

Though the easiest is a command-line option, if some equivalent comment can work for vim or any of the other vi variants, that would be great, too.

update:

After some experimentation with strace, it seems the following command might do what I want for vim:

vim -n -c 'set viminfo=' /tmp/XXX/secret.ini

However, it does not work with --cmd, which was the option suggested, but only with -c. Moreover, I don't really understand what -n does, but just noticed that the man page said it will break recovery. So I'd still love to hear an answer from someone knowledgeable about vim, if indeed this even works. And of course solutions for the other vi variants would be welcome.

update 2:

The EXINIT environment variable might work for vi and nvi, if I set it to EXINIT=set dir=/tmp/XXX|set recdir=. nvi gives a warning about lack of recovery on startup, which is encouraging, while vi does drop files into the file system, but at least they are in /tmp which is often a memory file system.

  • I haven't understood everything you are trying to ask, but are you trying to tell vim to suppress swap files and not to create back up files? set noswapfile and set nobackup are respective settings for those... also, would suggest to migrate the question to vi.stackexchange.com – Sundeep Oct 27 '16 at 9:24
  • 3
    @Sundeep like vi.stackexchange.com/questions/6177/…? – muru Oct 27 '16 at 10:03
3

For vim you can move the location of the recovery file with the directory option.

set directory=/tmp/XXXXXXXX

This can be added to the command line for a single instance like this

mkdir -m700 /tmp/xyz
vim --cmd "set directory=/tmp/xyz" /path/to/secure.file
  • This --cmd command is super useful. Is there any way to set parameters like this in other vi editors? (I know how to set them by typing ":set", just not how to do it on the command line.) Actually, though, it appears that with vim no files get created under /var/tmp/vi.recover anyway, as it seems to create .swp files in the same directory. (Of course it would still be great to get confirmation of this and suppress the .swp files altogether...) – user3188445 Oct 27 '16 at 23:52
  • So for vim, I tried using vim --cmd "set noswapfile" --cmd "set directory=/tmp/XXX" +normal 24G19| /tmp/XXX/file.ini (to go to column 19 of line 24 and not leak the file contents). The good news is strace didn't reveal any writing to /var/tmp/vi.recover. The bad news is that it leaked a bunch of secrets into ~/.viminfo, which is even worse because that file is in a home directory that could easily get backed up to archival storage! – user3188445 Oct 28 '16 at 0:18
0

I can think of several options for giving users a reasonable editor with as much paranoia as possible. Since this is a security-sensitive application please take everything here with a grain of salt and please correct any mistakes.

I'd suggest doing one of these things as the default, but making it possible in a clearly documented way for people to use their own vim as a clearly documented option so they don't try to copy data out of the locked-down vi.

Also some of these suggestions (in particular messing with LD_PRELOAD) might be totally unacceptable, disabled (as much as that's possible), or limited in your environment.

Here's some potentially mutually exclusive options in no particular order,

  1. run the editor as an unprivileged user, like sudoedit does.
  2. provide your own editor with features you don't want patched or compiled out
  3. intercept calls to problematic libc functions with LD_PRELOAD.
  4. skip the user's .vimrc completely.

1) run the editor as an unprivileged user

If you have the ability to create additional users or demand that people using the software create a dedicated unprivileged user, then you can use the same trick that sudoedit uses: creating a temporary file and having the user edit that as an unprivileged user. I don't know how to guarantee that the temporary file never touches the disk.

It sounds like you're already doing something similar to this, but without the unprivileged user.

Here's a superuser answer explaining how sudoedit works.

Also, I'd recommend studying how the sudoedit functionality of sudo works. here's some relevant code.

2) bundle your own vi.

nvi is small and BSD licensed, you might be able to patch it to never make swap files or compile it without swapfile support. I don't actually know because building the last time I tried building nvi I couldn't figure out how to get its ancient version of autotools to detect OS X.

As part of the build of your main product you can take the hash of your nvi executable and bake it in as a build-time constant.

Emacs users can get a copy of mg. Here's a link to a fork of mg. The variant that actually lives in OpenBSD's source tree is ISC licensed. You might need to patch it because it sometimes shells out (for instance, to interact with cscope).

Nano users are out of luck, I think.

3) LD_PRELOAD

use LD_PRELOAD to direct ld.so (on Linux) to load a shim to intercept calls to libc functions such as fwrite, fork, &c.

4) skip the user's config options in vim, run your own minimal config instead.

This is the most bulletproof command line for vim that I can think of, but I might have missed something. I only got it from reading the vim man page and looking at my .vimrc.

  • -u NONE -- no initialization
  • -i NONE -- no .viminfo
  • -U NONE -- no initialization (gvim but hey you can never be too careful)
  • -Z -- start as if your argv[0] was rvim.
  • set nocompatible -- don't be vi-compatible.
  • set backspace=indent,eol,start -- make backspace more intuitive
  • set noexrc -- probably redunant, don't read any rc files.
  • set secure -- no autocmd, no shell, no write, display - - map commands.
  • set nobackup -- no backup options
  • set laststatus=2 -- show the file you're editing

So, here's the command line putting it all together.

vim -n -u NONE -i NONE -U NONE -Z \
        --cmd "set nocompatible | set backspace=indent,eol,start | set noexrc | set secure | set nomodeline | set nobackup | set laststatus=2" \
         --
  • Thanks for trying to help, but most of these don't solve my problem. I'm trying to avoid writing secrets to disk, and what username I run vi as has no bearing on that. Locking down the editor isn't really the goal, since the user has permission to see this database anyway. LD_PRELOAD or ptrace could help I suppose. – user3188445 Mar 26 at 21:17
  • @user3188445 if the unprivileged user only has permission to write to a path to a temporary file that you know is in memory (because it's backed by ramfs or something), does that allow you to use the file permission system to enforce the property you want? – Gregory Nisbet Mar 26 at 21:32
  • The premise is not correct, though, unless I run the thing in a chrooted environment. After all, some vi implementations write stuff to /var/tmp/vi.recover and some machines do not have a memory file system for /tmp. Anyway, it's a pretty high bar to have to distributed a setuid root program with an application, so unless it 100% nails the problem in a totally portable way, not worth it. – user3188445 Mar 28 at 5:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.