欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

LINUX磁盘加密之CRYPTO

程序员文章站 2022-07-03 11:39:42
近分析rc.sysinit启动脚本,正遇磁盘加密之部分,经一番细研,非三言二意可所得。故,捋整究探,分二段于下:挪步接踵设建为先,启动方法分析佐后。 使用crypto可以对多数类unix系统...
近分析rc.sysinit启动脚本,正遇磁盘加密之部分,经一番细研,非三言二意可所得。故,捋整究探,分二段于下:挪步接踵设建为先,启动方法分析佐后。

使用crypto可以对多数类unix系统中的块设备进行底层数据加密,即在文件系统之下加密。对块设备加密完成后,再对该设备进行文件系统的格式化处理。对加密后的设备进行使用,需通过打开设备(这是需要输入密码)、挂载设备两步来实现。加密设备的退卸,反之即可。

若需开机自动挂载(即在/etc/fstab里指明),密码读取的方式可以通过两种方法:手动输入、使用/etc/crypttab指定设备对应的密码文件,具体实现可见第二部分的启动方法分析。接下来首先看如何一步一步实现设备加密。

 

一、 块设备加密

a) 创建新的块设备(磁盘、分区、块设备的文件等);

LINUX磁盘加密之CRYPTO

b) 使用cryptsetup luksFormat命令把块设备格式化为加密设备,当需要输入yes时记得是大写;

LINUX磁盘加密之CRYPTO

c) 使用cryptsetup luksAddKey给加密设备添加密码文件;

LINUX磁盘加密之CRYPTO

所谓的密钥文件没有任何变化;

d) 使用cryptsetup luksOpen打开设备;

LINUX磁盘加密之CRYPTO

e) 格式化打开的设备;

LINUX磁盘加密之CRYPTO

LINUX磁盘加密之CRYPTO

f) 修改/etc/fstab使其开机自动挂载;

LINUX磁盘加密之CRYPTO

g) 修改/etc/crypttab使其开机时使用密码文件,自动解密;

LINUX磁盘加密之CRYPTO

h) 开机测试(reboot)

LINUX磁盘加密之CRYPTO

LINUX磁盘加密之CRYPTO

LINUX磁盘加密之CRYPTO

二、 存在于/etc/init.d/function文件中,有关于crypto的方法分析

a) 方法init_crypto();

通过文件重定向来使用 read

注意,这里”read”命令将会产生一种不直观的行为. 

 1) 重新从文件的开头开始读入变量. 

 2) 每个变量都设置成了以空白分割的字符串, 而不是之前的以整行的内容作为变量的值. 

 3) 而最后一个变量将会取得第一行剩余的全部部分(不管是否以空白分割). 

 4) 如果需要赋值的变量的个数比文件中第一行一空白分割的字符串的个数多的话, 那么这些变量将会被赋空值.

使用read命令读取Linux系统上的文件。 
每次调用read命令都会读取文件中的”一行”文本。当文件没有可读的行时,read命令将以非零状态退出。

局部变量 

    如果变量用 local 来声明,那么它只能在该变量声明的代码块(block of code)中可见.  

    这个代码块就是局部”范围”. 在一个函数内,局部变量意味着只能在函数代码块内它才 

    有意义.

 

Expression

Meaning

${#string}

Length of $string

 

${string:position}

Extract substring from $string at $position

${string:position:length}

Extract $length characters substring from $string at $position

 

${string#substring}

Strip shortest match of $substring from front of $string

${string##substring}

Strip longest match of $substring from front of $string

${string%substring}

Strip shortest match of $substring from back of $string

${string%%substring}

Strip longest match of $substring from back of $string

 

${string/substring/replacement}

Replace first match of $substring with $replacement

${string//substring/replacement}

Replace all matches of $substring with $replacement

${string/#substring/replacement}

If $substring matches front end of $string, substitute $replacement for $substring

${string/%substring/replacement}

If $substring matches back end of $string, substitute $replacement for $substring

 

expr match ”$string” ’$substring’

Length of matching $substring* at beginning of $string

expr ”$string” : ’$substring’

Length of matching $substring* at beginning of $string

expr index ”$string” $substring

Numerical position in $string of first character in $substring that matches

expr substr $string $position $length

Extract $length characters from $string starting at $position

expr match ”$string” ’\($substring\)’

Extract $substring* at beginning of $string

expr ”$string” : ’\($substring\)’

Extract $substring* at beginning of $string

expr match ”$string” ’.*\($substring\)’

Extract $substring* at end of $string

expr ”$string” : ’.*\($substring\)’

Extract $substring* at end of $string

LINUX磁盘加密之CRYPTO

图(1)

LINUX磁盘加密之CRYPTO

图(2)

LINUX磁盘加密之CRYPTO

图(3)

LINUX磁盘加密之CRYPTO

图(4)

LINUX磁盘加密之CRYPTO

1. __readlink() {

2. ls -bl ”$@” 2>/dev/null| awk ’{ print $NF }’

3. }  #参见图(5);

1. key_is_random() {

2. [ "$1" = "/dev/urandom" -o "$1" = "/dev/hw_random" \

3. -o "$1" = "/dev/random" ]

4. }

1. find_crypto_mount_point() {

2. local fs_spec fs_file fs_vfstype remaining_fields

3. local fs

4. while read fs_spec fs_file remaining_fields; do  #读取/etc/fstab文件中的值,赋值给它们三个;

5. if [ "$fs_spec" = "/dev/mapper/$1" ]; then

6. echo $fs_file

7. break;  #当带”/dev/mapper/”加上进来的值,等于fs_file从fstab中取的变量时,则退出;

8. fi

9. done < /etc/fstab

10. }

1. # Because of a chicken/egg problem, init_crypto must be run twice.  /var may be

2. # encrypted but /var/lib/random-seed is needed to initialize swap.

3. init_crypto() {

4.     local have_random dst src key opt mode owner params makeswap skip arg opt  #声明局部变量;

5.     local param value rc ret mke2fs mdir prompt mount_point  #声明局部变量;

6. 

7.     ret=0  #设定返回值默认为真;

8.     have_random=$1  #方法的第一个参数值赋给have_random;

9.     while read dst src key opt; do  #参考141行,把/etc/crypttab文件里面的值,按段分别赋于dst src key opt这四个变量;

10.         [ -z "$dst" -o "${dst#\#}" != "$dst" ] && continue  #若变量dst的值为空,或者变量dst里面有注释符“#”存在“${dst#\#}表示若dst变量中的第一个字符为#则去除该字符,若dst变量的第一个字符不是#符号,那么${dst#\#}将等于${dst}”,则跳过下面语句,直接进入下次循环(因为变量dst、src、key、opt的值都来自文件/etc/crypttab,所以当文件中不能提供合适的值时,该程序将会进入死循环,直接退出);

11.         [ -b "/dev/mapper/$dst" ] && continue;  #目标若为块设备,则直接退出(参考上一条语句);

12.         if [ "$have_random" = 0 ] && key_is_random ”$key”; then

13.             continue

14.         fi  #若have_random的值等于0(即调用init_crypto函数时的第一个参数),则判断变量key的值是否等于“/dev/urandom、/dev/hw_random、/dev/random”中的任意一个,若为真则直接退出;

15.         if [ -n "$key" -a "x$key" != "xnone" ]; then  #变量key不为空,以及key的值不为none则执行下面语句(变量前面加x不知为何);

16.             if test -e ”$key” ; then  #该文件存在则执行下面语句;

17.                 owner=$(ls -l $key | (read a b owner rest; echo $owner))  #赋值给owner,见图(3);

18.                 if ! key_is_random ”$key”; then  #若key的值不为“/dev/urandom、/dev/hw_random、/dev/random”中的任意一个,则执行下面语句;

19.                     mode=$(ls -l ”$key” | cut -c 5-10)  #截取key值中,文件的所属组及其他用户的权限描述符,并把值赋给mode变量;

20.                     if [ "$mode" != "------" ]; then

21.                        echo $”INSECURE MODE FOR $key”

22.                     fi  #若mode的值不等于“——”,则回显“ INSECURE MODE FOR”提示;

23.                 fi

24.                 if [ "$owner" != root ]; then

25.                     echo $”INSECURE OWNER FOR $key”

26.                 fi  #若owner的值不为root,则提示该文件的拥有者不够安全;

27.             else

28.                 echo $”Key file for $dst not found, skipping”

29.                 ret=1

30.                 continue  #若16行判断不成了(即key变量中的文件不存在)则输出“ Key file for $dst not found, skipping”,并直接退出;

31.             fi

32.         else

33.             key=”"  #若15行判断不成立(即变量key的值为空),则给它再赋个空值,该句在这里没多大意义,只是一个动作而已;

34.         fi

35.         params=”"

36.         makeswap=”"

37.         mke2fs=”"

38.         skip=”"  #初始化“ params、 makeswap、 mke2fs、 skip”这四个变量的值为空;

39.         # Parse the src field for UUID= and convert to real device names

40.         if [ "${src%%=*}" == "UUID" ]; then  #通过从尾至首的最长匹配去除符“%%”,去掉src变量中等号后面的所有字符,并与UUID比较是否相等,若为真则执行下面命令,参见图(4);

41.                 src=$(/sbin/blkid -t ”$src” -l -o device)  #只显示设备全路径,如“/dev/sdb5”等;

42.         elif [ "${src/^\/dev\/disk\/by-uuid\/}" != "$src" ]; then  #不理解,括号中是猜想(若src的值为”/dev/disk/by-uuid/616d4fda-fae4-41eb-8b58-42cbe49d1f0d”,也许脚本编写人员是想通过‘${src/^\/dev\/disk\/by-uuid\/}’使 src中起始部分为‘/dev/disk/by-uuid/’的值替换为空,故而”616d4fda-fae4-41eb-8b58-42cbe49d1f0d”不等于”/dev/disk/by-uuid/616d4fda-fae4-41eb-8b58-42cbe49d1f0d”,所以可以执行接下来的语句了,但在测试当中,发现变量中的^并不能指明所谓的起始位置,反而使得整个语句无法达到预期的目标——即替换不成功);

43.                 src=$(__readlink $src)  #获取设备全路径,并给src重赋值,参见图(5),获取的值可能是“../../sdb5”,在使用中可能有问题,因为不是绝对路径;

44.         fi

45.         # Is it a block device?

46.         [ -b "$src" ] || continue  #若不是块设备则直接退出;

47.         # Is it already a device mapper slave? (this is gross)

48.         devesc=${src##/dev/}  #把src值中的“/dev/”替换成空,使devesc变量值为设备名,如sdb5;

49.         devesc=${devesc//\//!}  #把devesc中的所有“/”替换为“!”;

50.         for d in /sys/block/dm-*/slaves ; do

51.             [ -e $d/$devesc ] && continue 2

52.         done  #若该设备存在于dm中,则直接跳出该循环,continue 2表示跳过本次循环后面所有循环;

53.         # Parse the options field, convert to cryptsetup parameters and

54.         # contruct the command line

55.         while [ -n "$opt" ]; do  #当opt变量不为空时执行下面操作;

56.             arg=${opt%%,*}  #从尾至首删除opt中“,”及“,”后面的所有值,并把结果赋给arg(即第一个“,”前的参数);

57.             opt=${opt##$arg}  #即把第一个“,”及“,”后面的所有值赋给opt;

58.             opt=${opt##,}  #去掉其中所有的“,”;

59.             param=${arg%%=*}  #把等号前面的值赋给param;

60.             value=${arg##$param=}  #把等号后面的值赋给value;

61. 

62.             case ”$param” in  #case语句;

63.             cipher)  #指定密码的加密算法;

64.                 params=”$params -c $value”  #由于params的初始值为空,当param值等于cipher时,params的值为“-c $value”;

65.                 if [ -z "$value" ]; then

66.                     echo $”$dst: no value for cipher option, skipping”

67.                     skip=”yes”

68.                 fi  #如果value的值为空,则输出以上字符串,并把skip的值设为yes;

69.             ;;

70.             size)  #指定密码长度,一般是128、192或256位;

71.                 params=”$params -s $value”  #简单复制语句;

72.                 if [ -z "$value" ]; then

73.                     echo $”$dst: no value for size option, skipping”

74.                     skip=”yes”  #若value为空,则产生提示,并设skip为yes;

75.                 fi

76.             ;;

77.             hash)  #密码hash算法;

78.                 params=”$params -h $value”

79.                 if [ -z "$value" ]; then

80.                     echo $”$dst: no value for hash option, skipping”

81.                     skip=”yes”

82.                 fi  #若value为空,则产生提示,并设skip为yes;

83.             ;;

84.             verify)  #当从控制台输入密码时,要求输入两次;

85.                 params=”$params -y”

86.             ;; 

87.             swap)  #指定该分区为交换分区,当指定后,程序会把加密过的swap设备摧毁,并重新生成新的加密设备(swap);

88.                 makeswap=yes

89.                 ;;

90.             tmp)  #该加密后的设备将被tmp使用,并会把权限模式设为01777; 

91.                 mke2fs=yes

92.             esac

93.         done

94.         if [ "$skip" = "yes" ]; then

95.             ret=1

96.             continue

97.         fi  #如果skip的值为yes ,那么返回值为1(错误),并退出;

98.         if [ -z "$makeswap" ] && cryptsetup isLuks ”$src” 2>/dev/null ; then  #如果makeswap变量的值为空,且该驱动器属于加密设备,则执行下面语句;

99.             if key_is_random ”$key”; then

100.                 echo $”$dst: LUKS requires non-random key, skipping”

101.                 ret=1

102.                 continue

103.             fi  #当key的值属于“/dev/urandom、/dev/hw_random、/dev/random”中的任意一个,则直接退出;

104.             if [ -n "$params" ]; then

105.                 echo ”$dst: options are invalid for LUKS partitions,” \

106.                     ”ignoring them”

107.             fi  #当params的值不为空时,提示该分区有问题,将被忽略;

108.             if [ -n "$key" ]; then

109.                 /sbin/cryptsetup -d $key luksOpen ”$src” ”$dst” <&1 2>/dev/null && success || failure  #通过-d参数来指明,使用一文件作为密钥参数来解密,并打开设备,名称为“/dev/mapper/$dst”,执行成功则输出[ OK ]字样,失败输出[ FAILED ]字样;

110.                 rc=$?  #命令执行结果;

111.             else

112.                 mount_point=”$(find_crypto_mount_point $dst)”  #确认挂载点在fstab中被声明,若被声明,则把值赋给mount_point,否则mount_point值为空;

113.                 [ -n "$mount_point" ] || mount_point=${src##*/}  #当mount_point值为空时,跳过该语句;否则把src变量中,最后一个/后面的值赋给mount_point;

114.                 prompt=$(printf $”%s is password protected” ”$mount_point”)  #通过printf内置工具格式化“挂载点受密码保护”的信息,赋值给prompt;

115.                 plymouth ask-for-password –prompt ”$prompt” –command=”/sbin/cryptsetup luksOpen -T1 $src $dst” <&1  应该是通过plymouth弹出图形化的密码提示框,要求输入密码;

116.                 rc=$?  #退出值;

117.             fi

118.         else

119.             [ -z "$key" ] && plymouth –hide-splash  #若key的值为空,则隐藏plymouth的主题;

120.             /sbin/cryptsetup $params ${key:+-d $key} create ”$dst” ”$src” <&1 2>/dev/null && success || failure  #用cryptsetup通过驱动器、目标名称创建驱动器映射(中间’${key:+-d $key}’的key设置或没设置,都将加-d参数到原先值前面),执行成功输出ok字样,失败输出failed字样;

121.             rc=$?  #返回值给rc;

122.             [ -z "$key" ] && plymouth –show-splash  #若key值为空,则显示主题;

123.         fi

124.         if [ $rc -ne 0 ]; then  #当rc的值不为零时执行下面语句;

125.             ret=1  #设置ret为1;

126.             continue

127.         fi

128.         if [ -b "/dev/mapper/$dst" ]; then  #映射过来的目标为块设备,则执行下面语句;

129.             if [ "$makeswap" = "yes" ]; then  #当makeswap的值为yes时,执行里面语句;

130.                 mkswap ”/dev/mapper/$dst” 2>/dev/null >/dev/null  #格式化为交换分区;

131.             fi

132.             if [ "$mke2fs" = "yes" ]; then  #当mke2fs值为yes,执行里面语句;

133.                 if mke2fs ”/dev/mapper/$dst” 2>/dev/null >/dev/null \

134.                     && mdir=$(mktemp -d /tmp/mountXXXXXX); then  #格式化目标为ext2文件系统,并创建暂存目录/tmp/mountXXXXXX;

135.                     mount ”/dev/mapper/$dst” ”$mdir” && chmod 1777 ”$mdir”  #挂载设备到暂存目录,并设权限为1777;

136.                     umount ”$mdir”  #卸载暂存目录;

137.                     rmdir ”$mdir”  #删除暂存目录;

138.                 fi

139.             fi

140.         fi

141.     done < /etc/crypttab  #从/etc/crypttab文件读入值做为第9行变量的参数;

142.     return $ret  #返回值;

143. }