5

How can I pipe an output of a command just in case it returns true?

function open
{
    TEMPFILE=$(mktemp -u)
    if ! gpg2 --quiet --decrypt --batch --passphrase "$2" "$1" 2> $TEMPFILE; then
        error $"Password errata od errore di lettura dal file\n\nDettagli:\n$(grep -v '^$' $TEMPFILE)"
        rm -f $TEMPFILE
        return 1
    fi
    rm -f $TEMPFILE
}

if ! open "$@" "$PASSWORD"; then
    exit 1
fi | <SOMECOMMAND>

This way, it just pipe and don't check whether open returns true or false so doesn't ever execute "exit 1".

How can I solve it without using files (for security reasons).

Stefano d'Antonio
  • 5,874
  • 3
  • 32
  • 45

3 Answers3

9

Before I propose a solution, let me explain any this is more difficult than you realize. The basic problem is timing: the open ... function produces output as it runs; it produces an exit status after it has finished running (and hence after it has produced its output). Since you want to do different things with the output depending on the exit status, you must store the output someplace temporary until the function finishes and you can decide what to do with the output.

A pipe inherently won't work for this, because pipes don't store data (except for a little buffer space) -- they pass data "live" from one program to another, and in this case the second program can't start until after the first has finished. Normally, a temp file would be perfect for this (storing data is what files are for), but you don't want that for security reasons. That pretty much leaves putting the data somewhere in RAM (although that's not perfectly secure either...).

@Karoly Horvath's answer proposed storing the output in a bash variable (which is stored in RAM), but that didn't work because bash doesn't cope with null bytes in variable values. So, I propose a variant where you use a "safe" encoding of the data, and put that in a bash variable. I used uuencode format, but you could also use base64, hex dump, etc...

if result=$(open "$@" "$PASSWORD" | uuencode -; exit ${PIPESTATUS[0]}); then
    echo "$result" | uudecode -p | SOMECOMMAND
fi

Note that PIPESTATUS is a bashism, so you should start the script with #!/bin/bash. Also, if the output is too long you may run into limits on how much data bash wants to store/expand/etc; if that turns out to be a problem, things get more complicated.

BTW, if you're concerned about security, don't use gpg2's --passphrase option -- passing the passphrase on the command line exposes it to e.g. anyone who runs ps at the right time, which is a very bad idea. gpg2 has many options for supplying the passphrase, so please use a better one.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • Thanks, later I try using uuencode and let you know. Thanks also for the explanation; now I will use "echo $PASS | gpg2 -d --batch --passphrase-fd 0 $FILE", it is safe, right? – Stefano d'Antonio May 02 '12 at 21:31
  • However, while I'm looking for an alternative, I was using the "open" function two times, once to check (>/dev/null) and another one to use the data... But it is a waste of resources and time. (However, the passphrase is in ps just for a few second but you're absolutely right about I should not use --passphrase) – Stefano d'Antonio May 02 '12 at 21:33
  • 1
    Yes, using `echo "$PASSWORD" | gpg2 ... --passphrase-fd 0` (note the double-quotes around `$PASSWORD`) should be safe, because `echo` doesn't run as a regular command but as a builtin in a subshell, so its argument isn't visible to `ps` and the like. – Gordon Davisson May 02 '12 at 22:20
  • 1
    In this situation, that looks like the best way to do it. – Gordon Davisson May 03 '12 at 14:22
  • I want my software to have less dependecy possible, so I used base64... It works great!! Really thanks! Also for the $PIPESTATUS I didn't know! – Stefano d'Antonio May 03 '12 at 18:26
  • Actually, I'd expect uuencode to be more widely available than any particular base64 tool (although recent versions of the uuencode utility support base64 encoding with the `-m` flag). – Gordon Davisson May 03 '12 at 19:01
  • In my Gentoo, base64 is in coreutils, uuencode in sharutils, which is less necessary for the system (infact, I don't have it). – Stefano d'Antonio May 03 '12 at 23:08
  • Weird; uuencode has been in the single unix specification since [at least version 2 (1997)](http://pubs.opengroup.org/onlinepubs/7908799/xcuix.html), while base64 isn't even in [pretty recent versions](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html). Oh, well, go with what works in your situation... – Gordon Davisson May 03 '12 at 23:41
  • http://en.wikipedia.org/wiki/GNU_Core_Utilities http://en.wikipedia.org/wiki/GNU_Sharutils – Stefano d'Antonio May 04 '12 at 07:42
0

The following code should conditionally pipe the result if the file opens successfully:

   result=open "$@" "$PASSWORD"
    if [ $? -gt 0 ]; then
      exit 1
    fi
    echo "$result" | <SOMECOMMAND>
octopusgrabbus
  • 10,555
  • 15
  • 68
  • 131
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • open's output contain new line and other non printeable character, it doesn't work like this. – Stefano d'Antonio May 02 '12 at 12:00
  • new line is not an issue... what's your SOMECOMMAND? – Karoly Horvath May 02 '12 at 12:03
  • You're right about the new line, but it contains also other non printeable character (like \0) which are not preserved. The SOMECOMMAND is a function which print the passwords (through zenity if graphical, stdout if not). I can't use a file for security reason. – Stefano d'Antonio May 02 '12 at 12:39
  • 1
    @Uno You always have to quote subprocess's output. try `result="$(open $@ $PASSWORD)"` (i hate ticks) – KurzedMetal May 02 '12 at 13:24
  • @KurzedMetal: `open` would need to print the return code, i.e. `printf "1\n"f` for the value to be captured by cmd-substitution (i.e. `result=$(...)`). `return` only sets the value of the caller's `$?`. It's likely that as long as the assignment `result=...whatever` doesn't cause a syntax error that the value of `$?` will now reflect the successful assignment to `result` and the 'return'ed value is lost. Don't have time to test it. Good luck to all. – shellter May 02 '12 at 13:54
  • no, assignment is internal, doesn't change `$?`. also backticks work fine, quoting won't help. – Karoly Horvath May 02 '12 at 14:22
  • @shellter $? return the exit code of open, even if it's in a subshell. – Stefano d'Antonio May 02 '12 at 21:20
  • @Uno : shall I remove my comment? Good luck. – shellter May 02 '12 at 21:24
  • @shellter No, it surely doesn't disturb! :) Thank you anyway! – Stefano d'Antonio May 02 '12 at 21:38
0

Use a named pipe and store the return value

mkfifo piper;
open "$@" "$PASSWORD"; retval=$? > piper &
if [ x"$retval" != x0 ]
then
   rm piper
   exit 1
fi
<SOMECOMMAND> < piper
rm piper
Drake Clarris
  • 1,047
  • 6
  • 10
  • Won't work. First, retval gets set in a subshell (because of the `&`). Second, the `open ...` command will probably block waiting for data to be read from the pipe, but nothing can read from the pipe until after `open ...` finishes. – Gordon Davisson May 02 '12 at 15:46
  • I just tested this (although not using open obviously) and it worked as expected... both on failure of the command being sent to pipe and success, entering the then statement when it should. And the open command is writing to the pipe, not reading – Drake Clarris May 02 '12 at 15:49
  • Didn't work for me. I tried `false; retval=$? >/dev/null &`, waited for it to finish, then `echo $retval` and got a blank line. As for the open command blocking, if you try to write too much to a pipe (generally 64kB, see [here](http://stackoverflow.com/questions/4624071/pipe-buffer-size-is-4k-or-64k)), the writing process will block waiting for something to read the data and make room in the pipe. – Gordon Davisson May 02 '12 at 16:17
  • Just realized I had over simplified my test to the point of removing the &.. oops, my mistake – Drake Clarris May 02 '12 at 17:24
  • As i told, I don't wanna use neither file but also nor named pipes, the content is full of clear text passwords. – Stefano d'Antonio May 02 '12 at 21:22