forked from boyska/Tomb
-
Notifications
You must be signed in to change notification settings - Fork 1
/
tomb
executable file
·2569 lines (2174 loc) · 80.3 KB
/
tomb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/zsh
#
# Tomb, the Crypto Undertaker
#
# A commandline tool to easily operate encryption of secret data
#
# Homepage on: [tomb.dyne.org](http://tomb.dyne.org)
# {{{ License
# Copyright (C) 2007-2014 Dyne.org Foundation
#
# Tomb is designed, written and maintained by Denis Roio <[email protected]>
#
# With contributions by Anathema, Boyska and Hellekin O. Wolf.
#
# Testing and reviews are contributed by Dreamer, Shining,
# Mancausoft, Asbesto Molesto and Nignux.
#
# Tomb's artwork is contributed by Jordi aka Mon Mort.
#This source code is free software; you can redistribute it
#and/or modify it under the terms of the GNU Public License
#as published by the Free Software Foundation; either
#version 3 of the License, or (at your option) any later
#version.
#
#This source code is distributed in the hope that it will be
#useful, but WITHOUT ANY WARRANTY; without even the implied
#warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#PURPOSE. Please refer to the GNU Public License for more
#details.
#
#You should have received a copy of the GNU Public License
#along with this source code; if not, write to: Free
#Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
#02139, USA.
# }}} - License
# {{{ Global variables
VERSION=1.6
DATE="Sept/2014"
TOMBEXEC=$0
typeset -a OLDARGS
for arg in ${argv}; do OLDARGS+=($arg); done
DD="dd"
WIPE="rm -f"
MKFS="mkfs.ext3 -q -F -j -L"
KDF=1
STEGHIDE=1
RESIZER=1
SWISH=1
QRENCODE=1
MOUNTOPTS="rw,noatime,nodev"
# prefix for temporary files
TMPPREFIX="/dev/shm/$$.$RANDOM."
# makes glob matching case insensitive
unsetopt CASE_MATCH
typeset -AH global_opts
typeset -AH opts
typeset -H username
typeset -H _uid
typeset -H _gid
typeset -H _tty
typeset -H tomb_file
typeset -H tomb_key
typeset -H tomb_key_file
typeset -H tomb_secret
typeset -H tomb_password
typeset -aH tomb_tempfiles
typeset -aH tomb_loopdevs
# Make sure sbin is in PATH
PATH+=:/sbin:/usr/sbin
# For gettext
export TEXTDOMAIN=tomb
# }}}
# {{{ Safety functions
endgame() {
# here clear all temp files and flush all pipes
# prepare some random material to overwrite vars
rr="$RANDOM"
while [[ ${#rr} -lt 500 ]]; do
rr+="$RANDOM"; done
# we make sure no info is left in unallocated mem
tomb_file="$rr"; unset tomb_file
tomb_key="$rr"; unset tomb_key
tomb_key_file="$rr"; unset tomb_key_file
tomb_secret="$rr"; unset tomb_secret
tomb_password="$rr"; unset tomb_password
for f in $tomb_tempfiles; do
${=WIPE} "$f"; done
unset tomb_tempfiles
for l in $tomb_loopdevs; do
losetup -d "$l"; done
unset tomb_loopdevs
}
# trap functions for the endgame event
TRAPINT() { endgame INT }
TRAPEXIT() { endgame EXIT }
TRAPHUP() { endgame HUP }
TRAPQUIT() { endgame QUIT }
TRAPABRT() { endgame ABORT }
TRAPKILL() { endgame KILL }
TRAPPIPE() { endgame PIPE }
TRAPTERM() { endgame TERM }
TRAPSTOP() { endgame STOP }
check_shm() {
# TODO: configure which tmp dir to use from a cli flag
SHMPREFIX=/dev/shm
[[ -k /dev/shm ]] || [[ -k /run/shm ]] && { SHMPREFIX=/run/shm } \
|| {
# mount the tmpfs if the SO doesn't already
mkdir /run/shm
[[ $? = 0 ]] || {
fatal "Fatal error creating a directory for temporary files"
return 1 }
mount -t tmpfs tmpfs /run/shm \
-o nosuid,noexec,nodev,mode=0600,uid="$_uid",gid="$_gid"
[[ $? = 0 ]] || {
fatal "Fatal error mounting tmpfs in /run/shm for temporary files"
return 1 }
SHMPREFIX=/run/shm
}
# setup a special env var for zsh to create temp files that will
# then be deleted at the exit of each function using them.
TMPPREFIX="$SHMPREFIX/$$.$RANDOM."
return 0
}
# Provide a random filename in shared memory
tmp_create() {
local tfile="${TMPPREFIX}${RANDOM}"
touch "$tfile"
[[ $? = 0 ]] || {
fatal "Fatal error creating a temporary file: ::1 temp file::" $tfile
return 1 }
chown "$_uid":"$_gid" "$tfile"
chmod 0600 "$tfile"
[[ $? = 0 ]] || {
fatal "Fatal error setting permissions on temporary file: ::1 temp file::" $tfile
return 1 }
_verbose "Created tempfile: ::1 temp file::" $tfile
tomb_tempfiles+=($tfile)
return 0
}
tmp_new() {
# print out the latest tempfile
print - "${tomb_tempfiles[${#tomb_tempfiles}]}"
}
# Check if swap is activated
check_swap() {
# Return 0 if NO swap is used, 1 if swap is used
# Return 2 if swap(s) is(are) used, but ALL encrypted
local swaps="$(awk '/^\// { print $1 }' /proc/swaps 2>/dev/null)"
[[ -z "$swaps" ]] && return 0 # No swap partition is active
# Check whether all swaps are encrypted, and return 2
# If any of the swaps is not encrypted, we bail out and return 1.
ret=1
for s in $=swaps; do
bone=`sudo file $s`
if [[ "$bone" =~ "swap file" ]]; then
# It's a regular (unencrypted) swap file
ret=1
break
elif [[ "$bone" =~ "symbolic link" ]]; then
# Might link to a block
ret=1
if [ "/dev/mapper" = "${s%/*}" ]; then
is_crypt=`sudo dmsetup status "$s" | awk '/crypt/ {print $3}'`
if [ "crypt" = "$is_crypt" ]; then
ret=2
fi
else
break
fi
elif [[ "$bone" =~ "block special" ]]; then
# Is a block
ret=1
is_crypt=`sudo dmsetup status "$s" | awk '/crypt/ {print $3}'`
if [ "crypt" = "$is_crypt" ]; then
ret=2
else
break
fi
fi
done
_warning "An active swap partition is detected, this poses security risks."
if [[ $ret -eq 2 ]]; then
_success "All your swaps are belong to crypt. Good."
else
_warning "You can deactivate all swap partitions using the command:"
_warning " swapoff -a"
_warning "But if you want to proceed like this, use the -f (force) flag."
_failure "Operation aborted."
fi
return $ret
}
# Wrapper to allow encrypted swap and remind the user about
# possible data leaks to disk if swap is on, and not to be ignored
_check_swap() {
if ! option_is_set -f && ! option_is_set --ignore-swap; then
check_swap
case $? in
0|2) # No, or encrypted swap
return 0
;;
*) # Unencrypted swap
return 1
;;
esac
fi
}
# Ask user for a password
ask_password() {
# we use pinentry now
# comes from gpg project and is much more secure
# it also conveniently uses the right toolkit
# pinentry has no custom icon setting
# so we need to temporary modify the gtk theme
if [ -r /usr/local/share/themes/tomb/gtk-2.0-key/gtkrc ]; then
GTK2_RC=/usr/local/share/themes/tomb/gtk-2.0-key/gtkrc
elif [ -r /usr/share/themes/tomb/gtk-2.0-key/gtkrc ]; then
GTK2_RC=/usr/share/themes/tomb/gtk-2.0-key/gtkrc
fi
title="Insert tomb password."
if [ $2 ]; then title="$2"; fi
output=`cat <<EOF | GTK2_RC_FILES=${GTK2_RC} pinentry 2>/dev/null | tail -n +7
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE $title
SETDESC $1
SETPROMPT Password:
GETPIN
EOF`
if [[ `tail -n1 <<<$output` =~ ERR ]]; then
return 1
fi
head -n1 <<<$output | awk '/^D / { sub(/^D /, ""); print }'
return 0
}
# Drop privileges
exec_as_user() {
if ! [ $SUDO_USER ]; then
exec $@[@]
return $?
fi
_verbose "exec_as_user '::1 user::': ::2::" $SUDO_USER ${(f)@}
sudo -u $SUDO_USER "${@[@]}"
return $?
}
#Escalate privileges
check_priv() {
# save original user
username=$USER
if [ $UID != 0 ]; then
_verbose "Using sudo for root execution of '::1 exec:: ::2 args::'." $TOMBEXEC ${(f)OLDARGS}
# check if sudo has a timestamp active
sudok=false
if ! option_is_set --sudo-pwd; then
if [ $? != 0 ]; then # if not then ask a password
cat <<EOF | pinentry 2>/dev/null | awk '/^D / { sub(/^D /, ""); print }' | sudo -S -v
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE Super user privileges required
SETDESC Sudo execution of Tomb ${OLDARGS[@]}
SETPROMPT Insert your USER password:
GETPIN
EOF
fi
else
_verbose "Escalating privileges using sudo-pwd."
sudo -S -v <<<`option_value --sudo-pwd`
fi
sudo "${TOMBEXEC}" -U ${UID} -G ${GID} -T ${TTY} "${(@)OLDARGS}"
exit $?
fi # are we root already
# make sure necessary kernel modules are loaded
modprobe dm_mod
modprobe dm_crypt
return 0
}
# check if a filename is a valid tomb
is_valid_tomb() {
_verbose "is_valid_tomb ::1 tomb file::" $1
# argument check
{ test "$1" = "" } && {
_warning "Tomb file is missing from arguments."; return 1 }
# file checks
{ test -r "$1" } || {
_warning "Tomb file not found: ::1 tomb file::" $1; return 1 }
{ test -f "$1" } || {
_warning "Tomb file is not a regular file: ::1 tomb file::" $1; return 1 }
{ test -s "$1" } || {
_warning "Tomb file is empty (zero length): ::1 tomb file::" $1; return 1 }
{ test -w "$1" } || {
_warning "Tomb file is not writable: ::1 tomb file::" $1; return 1 }
# check file type (if its a Luks fs)
file "$1" | grep -i "luks encrypted file" > /dev/null || {
_warning "File is not yet a tomb: ::1 tomb file::" $1}
# check if its already open
tombfile=`basename $1`
tombname=${tombfile%%\.*}
mount -l | grep "${tombfile}.*\[$tombname\]$" > /dev/null
{ test $? = 0 } && {
_warning "Tomb is currently in use: ::1 tomb name::" $tombname; return 1 }
_message "Valid tomb file found: ::1 tomb file::" $1
return 0
}
# $1 is the tomb file to be lomounted
lo_mount() {
tpath="$1"
is_valid_tomb "$tpath" || {
_failure "Loopback mount called on invalid tomb: ::1 path::" $tpath }
# check if we have support for loop mounting
losetup -f >& -
[[ $? = 0 ]] || {
_warning "Loop mount of volumes is not possible on this machine, this error"
_warning "often occurs on VPS and kernels that don't provide the loop module."
_warning "It is impossible to use Tomb on this machine at this conditions."
_failure "Operation aborted."
}
_nstloop=`losetup -f` # get the number for next loopback device
losetup -f "$tpath" >& - # allocates the next loopback for our file
tomb_loopdevs+=("$_nstloop") # add to array of lodevs used
return 0
}
# print out latest loopback mounted
lo_new() { print - "${tomb_loopdevs[${#tomb_loopdevs}]}" }
# $1 is the path to the lodev to be preserved after quit
lo_preserve() {
_verbose "lo_preserve on ::1 path::" $1
# remove the lodev from the tomb_lodevs array
tomb_loopdevs=("${(@)tomb_loopdevs:#$1}")
}
# eventually used for debugging
dump_secrets() {
_verbose "tomb_file: ::1 tomb file::" $tomb_file
_verbose "tomb_key: ::1 key:: chars long" ${#tomb_key}
_verbose "tomb_key_file: ::1 key::" $tomb_key_file
_verbose "tomb_secret: ::1 secret:: chars long" ${#tomb_secret}
_verbose "tomb_password: ::1 tomb pass::" $tomb_password
_verbose "tomb_tempfiles: ::1 temp files::" ${(@)tomb_tempfiles}
_verbose "tomb_loopdevs: ::1 loopdevs::" ${(@)tomb_loopdevs}
}
# }}}
# {{{ Commandline interaction
usage() {
_print "Syntax: tomb [options] command [arguments]"
_print "\000"
_print "Commands:"
_print "\000"
_print " // Creation:"
_print " dig create a new empty TOMB file of size -s in MB"
_print " forge create a new KEY file and set its password"
_print " lock installs a lock on a TOMB to use it with KEY"
_print "\000"
_print " // Operations on tombs:"
_print " open open an existing TOMB"
_print " index update the search indexes of tombs"
_print " search looks for filenames matching text patterns"
_print " list list of open TOMBs and information on them"
_print " close close a specific TOMB (or 'all')"
_print " slam slam a TOMB killing all programs using it"
if [ "$RESIZER" = 1 ]; then
_print " resize resize a TOMB to a new size -s (can only grow)"
fi
_print "\000"
_print " // Operations on keys:"
_print " passwd change the password of a KEY"
_print " setkey change the KEY locking a TOMB (needs old one)"
_print "\000"
{ test "$QRENCODE" = "1" } && {
_print " engrave makes a QR code of a KEY to be saved on paper"
}
_print "\000"
if [ "$STEGHIDE" = 1 ]; then
_print " bury hide a KEY inside a JPEG image"
_print " exhume extract a KEY from a JPEG image"
fi
_print "\000"
_print "Options:"
_print "\000"
_print " -s size of the tomb file when creating/resizing one (in MB)"
_print " -k path to the key to be used ('-k -' to read from stdin)"
_print " -n don't process the hooks found in tomb"
_print " -o mount options used to open (default: rw,noatime,nodev)"
_print " -f force operation (i.e. even if swap is active)"
{ test "$KDF" = 1 } && {
_print " --kdf generate passwords armored against dictionary attacks"
}
_print "\000"
_print " -h print this help"
_print " -v print version, license and list of available ciphers"
_print " -q run quietly without printing informations"
_print " -D print debugging information at runtime"
_print "\000"
_print "For more informations on Tomb read the manual: man tomb"
_print "Please report bugs on <http://github.com/dyne/tomb/issues>."
}
# Check an option
option_is_set() {
# First argument, the commandline flag (i.e. "-s").
# Second (optional) argument: if "out", command will print it out 'set'/'unset'
# (useful for if conditions).
# Return 0 if is set, 1 otherwise
[[ -n ${(k)opts[$1]} ]];
r=$?
if [[ $2 == out ]]; then
if [[ $r == 0 ]]; then
echo 'set'
else
echo 'unset'
fi
fi
return $r;
}
# Get an option value
option_value() {
# First argument, the commandline flag (i.e. "-s").
print -n - "${opts[$1]}"
}
# Messaging function with pretty coloring
function _msg() {
local msg="$(gettext -s "$2")"
for i in $(seq 3 ${#});
do
msg=${(S)msg//::$(($i - 2))*::/$*[$i]}
done
local command="print -P"
local progname="$fg[magenta]${TOMBEXEC##*/}$reset_color"
local message="$fg_bold[normal]$fg_no_bold[normal]$msg$reset_color"
local -i returncode
case "$1" in
inline)
command+=" -n"; pchars=" > "; pcolor="yellow"
;;
message)
pchars=" . "; pcolor="white"; message="$fg_no_bold[$pcolor]$msg$reset_color"
;;
verbose)
pchars="[D]"; pcolor="blue"
;;
success)
pchars="(*)"; pcolor="green"; message="$fg_no_bold[$pcolor]$msg$reset_color"
;;
warning)
pchars="[W]"; pcolor="yellow"; message="$fg_no_bold[$pcolor]$msg$reset_color"
;;
failure)
pchars="[E]"; pcolor="red"; message="$fg_no_bold[$pcolor]$msg$reset_color"
returncode=1
;;
print)
progname=""
;;
*)
pchars="[F]"; pcolor="red"
message="Developer oops! Usage: _msg MESSAGE_TYPE \"MESSAGE_CONTENT\""
returncode=127
;;
esac
${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >&2
return $returncode
}
function _message say() {
local notice="message"
[[ "$1" = "-n" ]] && shift && notice="inline"
option_is_set -q || _msg "$notice" $@
return 0
}
function _verbose xxx() {
option_is_set -D && _msg verbose $@
return 0
}
function _success yes() {
option_is_set -q || _msg success $@
return 0
}
function _warning no() {
option_is_set -q || _msg warning $@
return 1
}
function _failure die() {
typeset -i exitcode=${exitv:-1}
option_is_set -q || _msg failure $@
# be sure we forget the secrets we were told
exit $exitcode
}
function _print() {
option_is_set -q || _msg print $@
return 0
}
# Print out progress to inform GUI caller applications (--batch mode)
progress() {
# $1 is "what is progressing"
# $2 is "percentage"
# $3 is (eventually blank) status
# Example: if creating a tomb, it could be sth like
# progress create 0 filling with random data
# progress create 40 generating key
# progress keygen 0 please move the mouse
# progress keygen 30 please move the mouse
# progress keygen 60 please move the mouse
# progress keygen 100 key generated
# progress create 80 please enter password
# progress create 90 formatting the tomb
# progress create 100 tomb created successfully
if ! option_is_set --batch; then
return
fi
print "[m][P][$1][$2][$3]" >&2
}
# Check what's installed
check_bin() {
# check for required programs
for req in cryptsetup pinentry sudo gpg; do
command -v $req >& - || exitv=1 _failure "Cannot find ::1::. It's a requirement to use Tomb, please install it." $req
done
export PATH=/sbin:/usr/sbin:$PATH
# which dd command to use
command -v dcfldd >& -
{ test $? = 0 } && { DD="dcfldd statusinterval=1" }
# which wipe command to use
command -v wipe >& - && WIPE="wipe -f -s" || WIPE="rm -f"
# check for filesystem creation progs
command -v mkfs.ext4 >& - && \
MKFS="mkfs.ext4 -q -F -j -L" || \
MKFS="mkfs.ext3 -q -F -j -L"
# check for steghide
command -v steghide >& - || STEGHIDE=0
# check for resize
command -v e2fsck resize2fs >& - || RESIZER=0
# check for KDF auxiliary tools
command -v tomb-kdb-pbkdf2 >& - || KDF=0
# check for Swish-E file content indexer
command -v swish-e >& - || SWISH=0
# check for QREncode for paper backups of keys
command -v qrencode >& - || QRENCODE=0
}
# }}} - Commandline interaction
# {{{ Key operations
# $1 is the encrypted key contents we are checking
is_valid_key() {
_verbose "is_valid_key"
_key="$1"
# argument check
{ test "$_key" = "" } && { _key="$tomb_key" }
{ test "$_key" = "" } && {
_warning "is_valid_key() called without argument."; return 1 }
# if the key file is an image don't check file header
{ test -r "$tomb_key_file" } \
&& [[ `file "$tomb_key_file"` =~ "JP.G" ]] \
&& {
_message "Key is an image, it might be valid."; return 0 }
[[ "$_key" =~ "BEGIN PGP" ]] && {
_message "Key is valid."; return 0 }
return 1
}
# $1 is a string containing an encrypted key
recover_key() {
_warning "Attempting key recovery."
_key="$tomb_key"
tomb_key=""
[[ "$_key" =~ "_KDF_" ]] && {
tomb_key+="`print - $_key | $head -n 1`\n" }
tomb_key+="-----BEGIN PGP MESSAGE-----\n"
tomb_key+="$_key\n"
tomb_key+="-----END PGP MESSAGE-----\n"
return 0
}
# This function retrieves a tomb key specified on commandline or from
# stdin if -k - was selected. It also runs validity checks on the
# file. On success returns 0 and prints out the full path to
# the key, setting globals: $tomb_key_file and $tomb_key
load_key() {
# take the name of a tomb file as argument to option -k
# if no argument is given, tomb{key|dir|file} are set by caller
local keyopt
[[ "$1" = "" ]] || { keyopt="$1" }
[[ "$keyopt" = "" ]] && { keyopt="`option_value -k`" }
[[ "$keyopt" = "" ]] && {
_failure "This operation requires a key file to be specified using the -k option." }
if [[ "$keyopt" == "-" ]]; then
_verbose "load_key reading from stdin."
# take key from stdin
_message "Waiting for the key to be piped from stdin... "
tomb_key_file=stdin
tomb_key=`cat`
else
_verbose "load_key argument: ::1 opt::" $keyopt
# take key from a file
tomb_key_file="$keyopt"
{ test -r "${tomb_key_file}" } || {
_warning "Key not found, specify one using -k."
return 1}
tomb_key=`cat $tomb_key_file`
fi
_verbose "load_key: ::1 key::" $tomb_key_file
is_valid_key "${tomb_key}" || {
_warning "The key seems invalid or its format is not known by this version of Tomb."
# try recovering the key
recover_key "$tomb_key"
}
# declared tomb_key (contents)
# declared tomb_key_file (path)
return 0
}
# takes two args just like get_lukskey
# prints out the decrypted content
# contains tweaks for different gpg versions
gpg_decrypt() {
# fix for gpg 1.4.11 where the --status-* options don't work ;^/
gpgver=`gpg --version --no-permission-warning | awk '/^gpg/ {print $3}'`
gpgpass="$1\n$tomb_key"
if [ "$gpgver" = "1.4.11" ]; then
_verbose "GnuPG is version 1.4.11 - adopting status fix."
tomb_secret=`print - "$gpgpass" | \
gpg --batch --passphrase-fd 0 --no-tty --no-options"`
ret=$?
unset gpgpass
else # using status-file in gpg != 1.4.11
# TODO: use mkfifo
tmp_create
_status=`tmp_new`
tomb_secret=`print - "$gpgpass" | \
gpg --batch --passphrase-fd 0 --no-tty --no-options \
--status-fd 2 --no-mdc-warning --no-permission-warning \
--no-secmem-warning 2> $_status`
unset gpgpass
grep 'DECRYPTION_OKAY' $_status > /dev/null
ret=$?
fi
return $ret
}
# Gets a key file and a password, prints out the decoded contents to
# be used directly by Luks as a cryptographic key
get_lukskey() {
# $1 is the password
_verbose "get_lukskey"
_password="$1"
exhumedkey=""
firstline=`head -n1 <<< "$tomb_key"`
# key is KDF encoded
if [[ $firstline =~ '^_KDF_' ]]; then
_verbose "KDF: ::1 kdf::" $(cut -d_ -f 3 <<<$firstline)
case `cut -d_ -f 3 <<<$firstline` in
pbkdf2sha1)
pbkdf2_param=`cut -d_ -f 4- <<<$firstline | tr '_' ' '`
_password=$(tomb-kdb-pbkdf2 ${=pbkdf2_param} 2>/dev/null <<<$_password)
;;
*)
_failure "No suitable program for KDF ::1 program::." $(cut -f 3 <<<$firstline)
unset _password
return 1
;;
esac
# key needs to be exhumed from an image
elif [ -r "$tomb_key_file" ] \
&& [[ `file "$tomb_key_file"` =~ "JP.G" ]]; then
exhume_key "$tomb_key_file" "$_password"
fi
gpg_decrypt "$_password" # saves decrypted content into $tomb_secret
ret="$?"
_verbose "get_lukskey returns ::1::" $ret
return $ret
}
# This function asks the user for the password to use the key it tests
# it against the return code of gpg on success returns 0 and saves
# the password in the global variable $tomb_password
ask_key_password() {
[[ "$tomb_key_file" = "" ]] && {
_failure "Internal error: ask_key_password() called before load_key()." }
keyname="$tomb_key_file"
_message "A password is required to use key ::1 key::" $keyname
passok=0
tombpass=""
if [[ "$1" = "" ]]; then
for c in 1 2 3; do
if [[ $c = 1 ]]; then
tombpass=`exec_as_user ${TOMBEXEC} askpass "Insert password to use key: $keyname"`
else
tombpass=`exec_as_user ${TOMBEXEC} askpass "Insert password to use key: $keyname (retry $c)"`
fi
if [[ $? != 0 ]]; then
_warning "User aborted password dialog."
return 1
fi
get_lukskey "$tombpass"
if [ $? = 0 ]; then
passok=1; _message "Password OK."
break;
fi
done
else
# if a second argument is present then the password is already known
tombpass="$1"
_verbose "ask_key_password with tombpass: ::1 tomb pass::" $tombpass
get_lukskey "$tombpass"
if [ $? = 0 ]; then
passok=1; _message "Password OK."; fi
fi
# print the password out in case caller needs to know it
{ test "$passok" = "1" } || { return 1 }
tomb_password="$tombpass"
return 0
}
# change tomb key password
change_passwd() {
_message "Commanded to change password for tomb key ::1 key::" $1
_check_swap
load_key
keyfile="$tomb_key_file"
local tmpnewkey lukskey c tombpass tombpasstmp
tmp_create
tmpnewkey=`tmp_new`
_success "Changing password for ::1 key file::" $keyfile
if option_is_set --tomb-old-pwd; then
tomb_old_pwd="`option_value --tomb-old-pwd`"
_verbose "tomb-old-pwd = ::1 old pass::" $tomb_old_pwd
ask_key_password "$tomb_old_pwd"
else
ask_key_password
fi
{ test $? = 0 } || {
_failure "No valid password supplied." }
# here $tomb_secret contains the key material in clear
if option_is_set --tomb-pwd; then
tomb_new_pwd="`option_value --tomb-pwd`"
_verbose "tomb-pwd = ::1 new pass::" $tomb_new_pwd
gen_key "$tomb_new_pwd" >> "$tmpnewkey"
else
gen_key >> "$tmpnewkey"
fi
if ! is_valid_key "`cat $tmpnewkey`"; then
_failure "Error: the newly generated keyfile does not seem valid."
else
# copy the new key as the original keyfile name
cp -f "${tmpnewkey}" "${keyfile}"
_success "Your passphrase was successfully updated."
fi
return 0
}
# takes care to encrypt a key
# honored options: --kdf --tomb-pwd -o
gen_key() {
# $1 the password to use, if not set then ask user
# -o is the --cipher-algo to use (string taken by GnuPG)
local algopt="`option_value -o`"
local algo="${algopt:-AES256}"
# here user is prompted for key password
tombpass=""
tombpasstmp=""
if [ "$1" = "" ]; then
while true; do
# 3 tries to write two times a matching password
tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password to secure your key"`
if [[ $? != 0 ]]; then
_failure "User aborted."
fi
if [ -z $tombpass ]; then
_warning "You set empty password, which is not possible."
continue
fi
tombpasstmp=$tombpass
tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password to secure your key (again)"`
if [[ $? != 0 ]]; then
_failure "User aborted."
fi
if [ "$tombpasstmp" = "$tombpass" ]; then
break;
fi
unset tombpasstmp
unset tombpass
done
else
tombpass="$1"
_verbose "gen_key takes tombpass from CLI argument: ::1 tomb pass::" $tombpass
fi
header=""
{ test "$KDF" = 1 } && {
{ option_is_set --kdf } && {
# KDF is a new key strenghtening technique against brute forcing
# see: https://github.com/dyne/Tomb/issues/82
itertime="`option_value --kdf`"
# removing support of floating points because they can't be type checked well
if [[ "$itertime" != <-> ]]; then
unset tombpass
unset tombpasstmp
_failure "Wrong argument for --kdf: must be an integer number (iteration seconds)."
fi
# --kdf takes one parameter: iter time (on present machine) in seconds
local -i microseconds
microseconds=$(( itertime * 10000 ))
_success "Using KDF, iterations: ::1 microseconds::" $microseconds
_message "generating salt"
pbkdf2_salt=`tomb-kdb-pbkdf2-gensalt`
_message "calculating iterations"
pbkdf2_iter=`tomb-kdb-pbkdf2-getiter $microseconds`
_message "encoding the password"
# We use a length of 64bytes = 512bits (more than needed!?)
tombpass=`tomb-kdb-pbkdf2 $pbkdf2_salt $pbkdf2_iter 64 <<<"${tombpass}"`
header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
}
}
print $header
# TODO: check result of gpg operation
cat <<EOF | gpg --openpgp --force-mdc --cipher-algo ${algo} \
--batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \
-o - -c -a
${tombpass}
${tomb_secret}
EOF
# print -n "${tombpass}" \
# | gpg --openpgp --force-mdc --cipher-algo ${algo} \
# --batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \
# -o - -c -a ${lukskey}
# update global var
tomb_password="$tombpass"
unset tombpass
unset tombpasstmp
}
# prints an array of ciphers available in gnupg (to encrypt keys)
list_gnupg_ciphers() {
# prints an error if GnuPG is not found
which gpg >& - || _failure "gpg (GnuPG) is not found, Tomb cannot function without it."
ciphers=(`gpg --version | awk '
BEGIN { ciphers=0 }
/^Cipher:/ { gsub(/,/,""); sub(/^Cipher:/,""); print; ciphers=1; next }
/^Hash:/ { ciphers=0 }
{ if(ciphers==0) { next } else { gsub(/,/,""); print; } }
'`)
echo " ${ciphers}"
return 1
}
# Steganographic function to bury a key inside an image.
# Requires steghide(1) to be installed
bury_key() {
load_key
[[ $? = 0 ]] || {
_failure "Bury failed for invalid key: ::1 key::" $tomb_key_file }
imagefile=$1
file $imagefile | grep -i JPEG > /dev/null
if [ $? != 0 ]; then
_warning "Encode failed: ::1 image file:: is not a jpeg image." $imagefile
return 1
fi
_success "Encoding key ::1 tomb key:: inside image ::2 image file::" $tombkey $imagefile
_message "Please confirm the key password for the encoding"
# We ask the password and test if it is the same encoding the
# base key, to insure that the same password is used for the
# encryption and the steganography. This is a standard enforced
# by Tomb, but its not strictly necessary (and having different
# password would enhance security). Nevertheless here we prefer
# usability.
if option_is_set --tomb-pwd; then
tomb_pwd="`option_value --tomb-pwd`"
_verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd
ask_key_password "$tomb_pwd"
else