Imported Upstream version 3.1.0
[debian/amanda] / common-src / amcrypt-ossl-asym.sh
1 #!@SHELL@
2 #
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16 #
17 # Amanda, The Advanced Maryland Automatic Network Disk Archiver
18 #
19 # Permission to use, copy, modify, distribute, and sell this software and its
20 # documentation for any purpose is hereby granted without fee, provided that
21 # the above copyright notice appear in all copies and that both that
22 # copyright notice and this permission notice appear in supporting
23 # documentation, and that the name of U.M. not be used in advertising or
24 # publicity pertaining to distribution of the software without specific,
25 # written prior permission.  U.M. makes no representations about the
26 # suitability of this software for any purpose.  It is provided "as is"
27 # without express or implied warranty.
28 #
29 # U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
30 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
31 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
32 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
33 # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
34 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 #
36 # Copyright (c) 2006  Ben Slusky <sluskyb@paranoiacs.org>
37
38
39 # amcrypt-ossl-asym.sh - asymmetric crypto helper using OpenSSL
40 # Usage: amcrypt-ossl-asym.sh [-d]
41 #
42
43 # Keys can be generated with the standard OpenSSL commands, e.g.:
44 #
45 # $ openssl genrsa -aes128 -out backup-privkey.pem 1024
46 # Generating RSA private key, 1024 bit long modulus
47 # [...]
48 # Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
49 # Verifying - Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
50 #
51 # $ openssl rsa -in backup-privkey.pem -pubout -out backup-pubkey.pem
52 # Enter pass phrase for backup-privkey.pem: <ENTER YOUR PASS PHRASE>
53 # Writing RSA key
54 #
55
56 prefix="@prefix@"
57 exec_prefix="@exec_prefix@"
58 sbindir="@sbindir@"
59 amlibexecdir="@amlibexecdir@"
60 . "${amlibexecdir}/amanda-sh-lib.sh"
61
62 # change these as needed
63 OPENSSL=                        # whatever's in $PATH
64 CIPHER=aes-256-cbc              # see `openssl help` for more ciphers
65 AMANDA_HOME=~@CLIENT_LOGIN@
66 RANDFILE=$AMANDA_HOME/.rnd
67 export RANDFILE
68 PASSPHRASE=$AMANDA_HOME/.am_passphrase  # optional
69 PRIVKEY=$AMANDA_HOME/backup-privkey.pem
70 PUBKEY=$AMANDA_HOME/backup-pubkey.pem
71
72 # where might openssl be?
73 PATH=/bin:/usr/bin:/usr/local/bin:/usr/ssl/bin:/usr/local/ssl/bin:/opt/csw/bin
74 export PATH
75 MAGIC='AmAnDa+OpEnSsL'
76 ME=`basename "$0"`
77 WORKDIR="/tmp/.${ME}.$$"
78
79
80 # first things first
81 if [ -z "${OPENSSL:=`which openssl`}" ]; then
82         echo `_ '%s: %s not found' "${ME}" "openssl"` >&2
83         exit 1
84 elif [ ! -x "${OPENSSL}" ]; then
85         echo `_ "%s: can't execute %s (%s)" "${ME}" "openssl" "${OPENSSL}"` >&2
86         exit 1
87 fi
88
89 if [ -n "${PASSPHRASE}" ]; then
90         # check the openssl version. if it's too old, we have to handle
91         # the pass phrase differently.
92         OSSL_VERSION=`eval \"${OPENSSL}\" version |cut -d\  -f2`
93         case "${OSSL_VERSION}" in
94          ''|0.[0-8].*|0.9.[0-6]*|0.9.7|0.9.7[a-c]*)
95                 echo `_ '%s: %s is version %s' "${ME}" "${OPENSSL}" "${OSSL_VERSION}"` >&2
96                 echo `_ '%s: Using pass phrase kluge for OpenSSL version >=0.9.7d' "${ME}"` >&2
97                 PASS_FROM_STDIN=yes
98                 ;;
99         esac
100 fi
101
102 mkdir -m 700 "${WORKDIR}"
103 if [ $? -ne 0 ]; then
104         echo `_ '%s: failed to create temp directory' "${ME}"` >&2
105         exit 1
106 fi
107 # ignore SIGINT
108 trap "" 2
109 trap "rm -rf \"${WORKDIR}\"" 0 1 3 15
110
111 # we'll need to pad the datastream to a multiple of the cipher block size
112 # prior to encryption and decryption. 96 bytes (= 768 bits) should be good
113 # for any cipher.
114 pad() {
115         perl -pe 'BEGIN { $bs = 96; $/ = \8192 } $nbytes = ($nbytes + length) % $bs; END { print "\0" x ($bs - $nbytes) }'
116 }
117
118 encrypt() {
119         # generate a random printable cipher key (on one line)
120         echo `"${OPENSSL}" rand -base64 80` >"${WORKDIR}/pass"
121
122         # encrypt the cipher key using the RSA public key
123         "${OPENSSL}" rsautl -encrypt -in "${WORKDIR}/pass" -out "${WORKDIR}/pass.ciphertext" -pubin -inkey "${PUBKEY}" -pkcs
124         [ $? -eq 0 ] || return 1
125
126         # print magic
127         printf "%s" "${MAGIC}"
128
129         # print the encrypted cipher key, preceded by size
130         ls -l "${WORKDIR}/pass.ciphertext" | awk '{ printf("%-10d", $5) }'
131         cat "${WORKDIR}/pass.ciphertext"
132
133         # encrypt data using the cipher key and print
134         pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -e -pass "file:${WORKDIR}/pass" -nosalt
135         [ $? -eq 0 ] || return 1
136 }
137
138 decrypt() {
139         # read magic
140         magicsize=`printf "%s" "${MAGIC}" | wc -c | sed 's/^ *//'`
141         magic=`dd bs=$magicsize count=1 2>/dev/null`
142         if [ "$magic" != "${MAGIC}" ]; then
143                 echo `_ '%s: bad magic' "${ME}"` >&2
144                 return 1
145         fi
146
147         # read size of encrypted cipher key
148         n=`dd bs=10 count=1 2>/dev/null`
149         [ $n -gt 0 ] 2>/dev/null
150         if [ $? -ne 0 ]; then
151                 echo `_ '%s: bad header' "${ME}"` >&2
152                 return 1
153         fi
154
155         # read the encrypted cipher key
156         dd "of=${WORKDIR}/pass.ciphertext" bs=$n count=1 2>/dev/null
157
158         # decrypt the cipher key using the RSA private key
159         if [ "${PASS_FROM_STDIN}" = yes ]; then
160                 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" -pkcs < "${PASSPHRASE}"
161         else
162                 "${OPENSSL}" rsautl -decrypt -in "${WORKDIR}/pass.ciphertext" -out "${WORKDIR}/pass" -inkey "${PRIVKEY}" ${PASSARG} -pkcs 3< "${PASSPHRASE}"
163         fi
164         [ $? -eq 0 ] || return 1
165
166         # use the cipher key to decrypt data
167         pad | "${OPENSSL}" enc "-${CIPHER}" -nopad -d -pass "file:${WORKDIR}/pass" -nosalt
168
169         # N.B.: in the likely event that we're piping to gzip, the above command
170         # may return a spurious error if gzip closes the output stream early.
171         return 0
172 }
173
174 if [ "$1" = -d ]; then
175         if [ -z "${PRIVKEY}" ]; then
176                 echo `_ '%s: must specify private key for decryption' "${ME}"` >&2
177                 exit 1
178         elif [ ! -r "${PRIVKEY}" ]; then
179                 echo `_ "%s: can't read private key from %s" "${ME}" "${PRIVKEY}"` >&2
180                 exit 1
181         fi
182
183         if [ -n "${PASSPHRASE}" -a -e "${PASSPHRASE}" -a -r "${PASSPHRASE}" ]; then
184                 PASSARG='-passin fd:3'
185         else
186                 PASSPHRASE=/dev/null
187         fi
188
189         decrypt
190         if [ $? -ne 0 ]; then
191                 echo `_ '%s: decryption failed' "${ME}"` >&2
192                 exit 1
193         fi
194 else
195         if [ -z "${PUBKEY}" ]; then
196                 echo `_ '%s: must specify public key for encryption' "${ME}"` >&2
197                 exit 1
198         elif [ ! -r "${PUBKEY}" ]; then
199                 echo `_ "%s: can't read public key from %s" "${ME}" "${PUBKEY}"` >&2
200                 exit 1
201         fi
202
203         encrypt
204         if [ $? -ne 0 ]; then
205                 echo `_ '%s: encryption failed' "${ME}"` >&2
206                 exit 1
207         fi
208 fi