changed dependencies to grml-shlib
[grml-crypt.git] / grml-crypt
1 #!/bin/sh
2 # Filename:      grml-crypt
3 # Purpose:       Program to format, mount and unmount encrypted devices/files
4 # Authors:       Michael Gebetsroither <gebi@grml.org>
5 # Bug-Reports:   see http://grml.org/bugs/
6 # License:       This file is licensed under the GPL v2.
7 # Latest change: Mon Aug 08 11:37:20 CEST 2005
8 ################################################################################
9
10
11 ###
12 ### __INCLUDES
13 ###
14 . /etc/grml/sh-lib
15 #. /etc/grml/sysexits-sh
16
17
18
19 ###
20 ### __VARIABLES
21 ###
22
23 verbose_=0
24 DEV_MAPPER_="/dev/mapper"
25 CRYPTSETUP_="cryptsetup"
26 IS_IMAGE_='false'
27 SIZE_="10"
28 SIZE_SET_='false'
29 FSTYPE_="vfat"
30 TARGET_=""
31 VERIFY_PW_=""
32 MKFS_=""
33 DM_NAME_=""
34 DM_PATH_=""
35 ACTION_=""
36 DM_PREFIX_="grml-crypt_"
37 FORCE_='false'
38 OVERWRITE_SOURCE_DEV_='/dev/urandom'
39 OPTIMIZED_MODE_SET_='false'
40 OPTIMIZING_LEVEL_=0
41 CIPHER_SIZE_="128"
42 CIPHER_="aes-cbc-essiv:sha256"
43 ITERATION_TIME_="1"
44 ADDITIONAL_CRYPTSETUP_ARGS_=""
45 READONLY_SET_='false'
46 ADDITIONAL_MOUNT_ARGS_=""
47 BATCH_MODE_="--batch-mode"
48
49 ###
50 ### __FUNCTIONS
51 ###
52
53 function printUsage
54 {
55   cat <<EOT
56 Usage: "$PROG_NAME__" [OPTIONS] action <device/file> [mountpoint]
57
58 $PROG_NAME__ is a wrapper arround cryptsetup with LUKS support to format a device
59
60 OPTIONS:
61    -s         size of the loop-filesystem to create, in MB (default=$SIZE_)
62    -t         type of filesystem (default=$FSTYPE_)
63    -r         read only mode (fully supported only by start)
64    -z         insecure mode, using /dev/zero for most of the initialisation (INSECURE!)
65    -o         optimized initialisation mode (should be as secure as the default but faster)
66    -y         verifies the passphrase by asking for it twice
67    -f         force file overwriting in format mode and/or disable confirmation dialog
68    -m         additional arguments to mount
69    -v         verbose (show what is going on, v++)
70    -h         this help text
71
72 CRYPTSETUP FORMAT OPTIONS:
73    -S         cipher size, could be 128, 192 or 256 (default=$CIPHER_SIZE_)
74    -C         cipher, should be aes-plain for pre-2.6.10 (default=$CIPHER_)
75    -I         iteration time spend with PBKDF2 password  processing in seconds (default=$ITERATION_TIME_)
76    -A         additional arguments for cryptsetup (only supportet by format)
77
78 ACTIONS:
79    format  <device/file> [mountpoint]
80               Format a device or a file (is created with the given size if it
81               does not exist) with the given filesystem and mount it, if a
82               mountpoint was given.
83    start  <device/file> <mountpoint>
84               Mount the device/file in the mountpoint.
85    stop   <mountpoint>
86               Umount the given mountpoint (umount, luksClose, losetup -d)
87
88 EOT
89 }
90
91
92 function getDMName
93 {
94   device_="${1##*/}"
95   
96   # first trying normal devicename
97   tmp_="${DM_PREFIX_}${device_}"
98   if [ ! -e "$tmp_" ]; then
99     echo "$tmp_"
100     return 0
101   fi
102
103   # second trying uuid of luks
104   #uuid_=`execute "$CRYPTSETUP_ luksUUID $1"`
105   #if [[ $? == 0 ]]; then
106   #  echo "$prefix_$uuid_"
107   #  return 0
108   #fi
109   warn "could not create device-mapper name for $1"
110   return 1
111 }
112
113
114 function formatDevice
115 {
116   type_="$1"  # could be donothing or init
117   ret_=0
118
119   args_="$VERIFY_PW_ $BATCH_MODE_ --key-size $CIPHER_SIZE_ --cipher $CIPHER_ --iter-time $ITERATION_TIME_ $ADDITIONAL_CRYPTSETUP_ARGS_"
120   #args_=`echo "$args_" |tr -s ' '`
121   execute "$CRYPTSETUP_ $args_ luksFormat $TARGET_" warn || return 1
122
123   execute "$CRYPTSETUP_ luksOpen $TARGET_ $DM_NAME_" warn \
124     "could not open $DM_PATH_ to create a filesystem on it!" || return 1
125   if [[ $type_ == 'init' && $OPTIMIZED_MODE_SET_ == 'true' ]]; then
126     echo "finishing optimized initialisation (this could take some time)"
127     # FIXME
128     execute "dd if=/dev/zero of=$DM_PATH_ bs=1M &>/dev/null" # || \
129     #  warn "could not finish optimized initialisation properly"
130     ret_=$?
131     # cutted out because of no space left on device error :(
132     #if [[ $ret_ != 0 ]]; then
133     #  execute "$CRYPTSETUP_ luksClose $DM_NAME_" warn
134     #  return 1
135     #fi
136   fi
137
138   execute "$MKFS_ $DM_PATH_ >/dev/null" warn
139   if [[ $? != 0 ]]; then
140     execute "$CRYPTSETUP_ luksClose $DM_NAME_"
141     warn "could not create filesystem on $DM_PATH_" 1
142     return 1
143   else
144     echo "Successully created $FSTYPE_ on encrypted $TARGET_"
145     return 0
146   fi
147
148
149
150 function actionStart
151 {
152   ret_=0
153   
154   # no mountpoint, by-by
155   if [[ "$MOUNT_POINT_" == "" ]]; then
156     printUsage
157     die 'no mountpoint given'
158   fi
159   if [ ! -d "$MOUNT_POINT_" ]; then
160     die "mountpoint $MOUNT_POINT_ does not exist"
161   fi
162   # removed due to unionfs problem isLuks does not work with filesystem images
163   # without losetup
164   #$CRYPTSETUP_ isLuks $TARGET_ || die "$TARGET_ is not a luks partition"
165
166   # TARGET (is/should be) a filesystem image
167   if [ ! -b "$TARGET_" ]; then
168     notice "Operating on a file"
169     isExistent "$TARGET_" die "image does not exist"
170     TARGET_=`findNextFreeLoop` || die "could not find a free loop device"
171
172     # TARGET_ is now /dev/loop<x>
173     execute "losetup $TARGET_ $ORIG_TARGET_" die
174   fi
175   cargs_=""
176   $READONLY_SET_ && cargs_='--readonly'
177   execute "$CRYPTSETUP_ $cargs_ luksOpen $TARGET_ $DM_NAME_" warn || execute "losetup -d $TARGET_" || \
178     die "could not luksOpen $TARGET_"
179   margs_=""
180   $READONLY_SET_ && margs_='-r'
181   execute "mount $margs_ $ADDITIONAL_MOUNT_ARGS_ $DM_PATH_ $MOUNT_POINT_" die
182 }
183
184
185 function actionStop
186 {
187   mp_="$1"
188   ret_=0
189
190   isExistent "$mp_" die
191   tmp_=`realpath $mp_` || die "could not get realpath of $mp_"
192   dprint "realpath_=\"$tmp_\""
193   
194   dm_path_=`mount |grep "$tmp_ "` || die "$tmp_ is not mounted"
195   dprint "dm_path_=\"$dm_path_\""
196   dm_path_=`echo $dm_path_ |awk '{print $1}'` || die "could not get devicemapper name for $tmp_"
197   dprint "dm_path_=\"$dm_path_\""
198   
199   dm_name_="${dm_path_##*/}"
200   dprint "dm_name_=\"$dm_name_\""
201
202   dmsetup info $dm_name_ >/dev/null ||die "$dm_name_ is not aktive"
203   device_=`$CRYPTSETUP_ status $dm_name_ |awk '/device:/{print $2}'` || \
204     die "could not get underlying device of $dm_path_"
205   dprint "device_=\"$device_\""
206   
207   execute "umount $dm_path_" die "could not unmount $device_"
208   execute "$CRYPTSETUP_ luksClose $dm_name_" die "could not close $dm_path_"
209   echo "$device_" |grep loop &>/dev/null && execute "losetup -d $device_" \
210     die "could not delete loop device $device_" || \
211     execute "losetup -d $device_ &>/dev/null" eprint "could not delete loop device $device_, \
212 this device could possible not be a loop device => maybe bogus error"
213   notice "$mp_ successfully unmountet/closed/deleted"
214 }
215
216 function yesDialog
217 {
218   msg_="$1"
219   
220   echo "WARNING!" >&2
221   echo "========" >&2
222   echo -n "$msg_" >&2
223   echo -n " (type uppercase yes): " >&2
224   read input
225   if [[ $input == 'YES' ]]; then
226     return 0
227   fi
228
229   return 1
230 }
231
232 function actionFormat
233 {
234   IS_IMAGE_='false'
235   ret_=0
236   init_='init'
237
238   if (( $SIZE_ < 3 )); then
239     die "the minimum size of an encrypted luks partition should be 2"
240   fi
241
242   # TARGET (is/should be) a filesystem image
243   if [ ! -b "$TARGET_" ]; then
244     notice "Operating on a file"
245     IS_IMAGE_='true'
246     if [ -e "$TARGET_" ]; then
247       $FORCE_ || die "file $TARGET_ does allready exist"
248       warn "overwriting file $TARGET_"
249       init_='donothing'
250     else
251       echo -n "Initialising file with "
252       if [[ $OPTIMIZED_MODE_SET_ == 'true' ]]; then
253         echo "optimized SECURE mode"
254         execute "dd if=/dev/zero of=$TARGET_ bs=1M count=${SIZE_} &>/dev/null" \
255           die "could not initialise $TARGET_ with /dev/zero"
256       else
257         if [[ $OVERWRITE_SOURCE_DEV_ == '/dev/zero' ]]; then
258           echo "INSERCURE mode"
259         else
260           echo "SECURE mode (taking /dev/urandom as source, this could take some time)"
261         fi
262         execute "dd if=$OVERWRITE_SOURCE_DEV_ of=$TARGET_ bs=1M count=${SIZE_} &>/dev/null" ||\
263           die "could not initialise $TARGET_ with $OVERWRITE_SOURCE_DEV_"
264       fi
265     fi
266
267     TARGET_=`findNextFreeLoop` || die "could not find a free loop device"
268
269     # TARGET_ is now /dev/loop<x>
270     execute "losetup $TARGET_ $ORIG_TARGET_" die
271     if [[ $OPTIMIZED_MODE_SET_ == 'true' || $OVERWRITE_SOURCE_DEV_ == '/dev/zero' ]]; then
272       execute "dd if=/dev/urandom of=$TARGET_ bs=1M count=2 &>/dev/null" \
273       die "could not initialise the fist 2MB of $TARGET_ with /dev/urandom"
274     fi
275     formatDevice "$init_"
276     ret_=$?
277   else
278     $FORCE_ || (yesDialog "Are you shure you want to overwrite $TARGET_ ?" || die 'You are not sure')
279     notice 'Operating on a device'
280     echo -n 'Initialising device with '
281     if [[ $OPTIMIZED_MODE_SET_ == 'true' ]]; then
282       echo "optimised SECURE mode"
283       execute "dd if=/dev/urandom of=$TARGET_ bs=1M count=2 &>/dev/null" ||\
284         die "could not initialise the first 2MB of $TARGET_ with /dev/urandom"
285     elif [[ $OVERWRITE_SOURCE_DEV_ != '/dev/zero' ]]; then
286       # default mode
287       echo "SECURE mode (taking /dev/urandom as source, this could take some time)"
288       execute "dd if=/dev/urandom of=$TARGET_ bs=1M &>/dev/null" ||\
289         die "could not initialise $TARGET_ with /dev/zero"
290     else
291       echo 'INSECURE mode (only initialising the fist 2MB with /dev/urandom)'
292       execute "dd if=/dev/urandom of=$TARGET_ bs=1M count=2 &>/dev/null" \
293         die "could not initialise the first 2MB of $TARGET_ with /dev/urandom"
294     fi
295
296     formatDevice "$init_"
297     ret_=$?
298   fi
299
300   # formatDevice was successfully
301   if (( $ret_ == 0 )); then
302     # a mountpoint was given (don't luksClose the device)
303     local mount_point_exists_='true'
304     test -d "$MOUNT_POINT_" || mount_point_exists_='false'
305
306     if [[ "$MOUNT_POINT_" != "" && "$mount_point_exists_" == 'true' ]]; then
307       $READONLY_SET_ && margs_='-r'
308       execute "mount $margs_ $ADDITIONAL_MOUNT_ARGS_ $DM_PATH_ $MOUNT_POINT_" die
309     else
310       $mount_point_exists_ || warn "mountpoint $MOUNT_POINT_ does not exist, not mounting. please use \"grml-crypt start $ORIG_TARGET_ <mountpoint>\" to start the device"
311       execute "$CRYPTSETUP_ luksClose $DM_NAME_" warn
312       $IS_IMAGE_ && execute "losetup -d $TARGET_" warn
313     fi
314   else
315     $IS_IMAGE_ && execute "losetup -d $TARGET_" warn
316   fi
317 }
318
319
320
321 ###
322 ### __MAIN
323 ###
324
325 while getopts "s:t:rzoyfm:hvS:C:I:A:" opt; do
326   case "$opt" in
327     s) SIZE_="$OPTARG"; SIZE_SET_='true' ;;
328     t) FSTYPE_="$OPTARG" ;;
329     r) READONLY_SET_='true' ;;
330     z) let OPTIMIZING_LEVEL_=$OPTIMIZING_LEVEL_+1
331         OVERWRITE_SOURCE_DEV_='/dev/zero'
332         warn 'initialising from INSECURE source /dev/zero' ;;
333     o) let OPTIMIZING_LEVEL_=$OPTIMIZING_LEVEL_+1
334         OPTIMIZED_MODE_SET_='true' ;;
335     y) VERIFY_PW_="--verify-passphrase" ;;
336     f) FORCE_='true' ;;
337     m) ADDITIONAL_MOUNT_ARGS_="$OPTARG" ;;
338     h) printUsage; exit ;;
339     v) let verbose_=$verbose_+1 ;;
340     S) CIPHER_SIZE_="$OPTARG" ;;
341     C) CIPHER_="$OPTARG" ;;
342     I) ITERATION_TIME_="$OPTARG" ;;
343     A) ADDITIONAL_CRYPTSETUP_ARGS_="$OPTARG" ;;
344     ?) printUsage; exit 64 ;;
345   esac
346 done
347 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
348 setVerbose $verbose_
349
350 checkRoot die "You have to be root to use this program"
351 disableSyslog
352
353 if [[ $1 == 'help' ]]; then
354   printUsage
355   exit 0
356 fi
357 if (( $# < 2 )); then
358   printUsage
359   die "wrong number of arguments ($#)" 1
360 fi
361 if (( $OPTIMIZING_LEVEL_ > 1 )); then
362   printUsage
363   die "please choose ONE initialisation methode"
364 fi
365 TARGET_="$2"
366
367 MKFS_="/sbin/mkfs.$FSTYPE_"
368 if [ ! -x "$MKFS_" ]; then
369   die "invalid filesystem type \"$FSTYPE_\"" 1
370 fi
371
372 # use batch-mode if available
373 $CRYPTSETUP_ $BATCH_MODE_ --help &>/dev/null;
374 ret_=$?
375 case "$ret_" in
376   0)  dprint "your cryptsetup understands --batch-mode" ;;
377   1)  BATCH_MODE_=""; notice "your cryptsetup does NOT understand --batch-mode, trying without" ;;
378   127)  die "could not execute cryptsetup" 127 ;;
379   *)  warn "problems executing $CRYPTSETUP_" $ret_
380 esac
381
382 DM_NAME_="`getDMName $TARGET_`"
383 DM_PATH_="$DEV_MAPPER_/$DM_NAME_"
384 ORIG_TARGET_="$TARGET_"
385 MOUNT_POINT_="$3"
386
387 case "$1" in
388   format) ACTION_='format'; actionFormat ;;
389   start)  ACTION_='start'; actionStart ;;
390   stop)  ACTION_='stop'; actionStop "$TARGET_" ;;
391   *)  printUsage ;;
392 esac
393
394 # END OF FILE
395 ################################################################################
396 # vim:foldmethod=marker