Linux透明加密文件系统1_代码分析
主要分为三个部分:
第一部分是交互脚本与自动化脚本(Ubuntu)
第二部分是界面设计
第三部分是开源项目代码的说明
用户使用的是基于QT的用户界面,可以选择需要加密的文件路径,已经加密之后的路径。当图形界面接收到这些信息之后,会将该信息转换成一段包含了8个参数的数据包传递给shell脚本进行处理。
一.交互脚本与自动化脚本
1. 处理交互式过程的脚本
#!/usr/bin/expect
#该文件是处理交互式过程的主要文件,用来对输出信息进行解析,并自动输入对应的信息
set timeout 2
set ecryptfs_real [lindex $argv 0]
#真实文件路径,实际的加密文件位置
set ecryptfs_mount [lindex $argv 1]
#挂载点路径,解密文件的位置
set tangpassword [lindex $argv 2]
#用户密码
set key_type1 [lindex $argv 3]
#密码管理方式
set Passphrase [lindex $argv 4]
#文件加密密码
set ecryptfs_type [lindex $argv 5]
#文件加密类型
set key_bytes [lindex $argv 6]
#文件加密位数
set ecryptfs_fspath [lindex $argv 7]
#路径是否加密
set ecryptfs_filename [lindex $argv 8]
#文件名是否加密
spawn sudo mount -t ecryptfs $ecryptfs_real $ecryptfs_mount#挂载加密文件系统
expect "password for tang:"
send "$tangpassword\n"
#填充用户密码
expect "Select key type to use for newly created files:"
send "$key_type1\n"
expect
#填充加密方式类型 "Passphrase:"
send "$Passphrase\n"
#填充文件加密密码
expect "aes"
send "$ecryptfs_type\n"
#填充文件加密类型
expect "Select key bytes:"
send "$key_bytes\n"
#填写文件加密位数
expect "Enable plaintext passthrough"
send "$ecryptfs_fspath\n"
#选择是否对路径加密
expect "Enable filename encryption"
send "$ecryptfs_filename\r"
interact
#选择是否对文件名加密
#expect eof
#exit
2. 与QT交互的脚本
#!/bin/bash
##该脚本主要作用是接收QT传递过来的变量,并进行解析,之后,调用相应的交互处理脚本##接收QT传递过来的变量
ECRYPTFS_REAL=$1 ##获取加密路径
PASSWD=$2 ##用户密码
PASS_TYPE=$3 ##密码类型
PASSPHRASE=$4 ##加密路径,挂载点
ECRYPTFS_TYPE=$5 ##加密类型
ECRYPTFS_BIT=$6 ##加密位数
ECRYPTFS_FS=$7 ##路径是否加密
ECRYPTFS_FILENAME=$8 ##文件名是否加密
count=0 ##初始值
dir="/tmp/mnt/ecryptfs$count" ##初始挂载点
echo "Ecryptfs加密程序启动中..."
echo "申请超级用户权限,请输入用户密码"
##/bin/testpass
df 1>/tmp/1.txt 2>/dev/null
##检测当前已经使用的挂载点,建立一个新的挂载点来使用
echo "已经挂载的加密目录有:"
while cat /tmp/1.txt | grep -q $dir 2>/dev/null
##挂载点 是否使用了
do
echo $dir
let count=$count+1 ##一定要是/bin/bash如是/bin/sh这里就会出错
dir="/tmp/mnt/ecryptfs$count" ###新挂载点
done
rm /tmp/1.txt
echo "新增挂载目录:" ###创建新的挂载点
if [ ! -d $dir ];then ##判断目录是否存在
mkdir -p $dir ###建立新挂载点目录,选项p,可以创建连续文件夹
fi
echo "加密程序启动,开始加密……
echo "请输入加密密码,选择加密方式:"
## sudo mount -t ecryptfs $(pwd) $dir ### $( )为引用命令结果
##调用交互脚本来处理和用户的交互过程
echo $ECRYPTFS_REAL $dir $PASSWD $ECRYPTFS_TYPE $ECRYPTFS_BIT $ECRYPTFS_FS $ECRYPTFS_FILENAME
/bin/automount $ECRYPTFS_REAL $dir $PASSWD $PASS_TYPE $PASSPHRASE $ECRYPTFS_TYPE $ECRYPTFS_BIT $ECRYPTFS_FS $ECRYPTFS_FILENAME
3. 挂载点操作脚本
#!/bin/bash
##用来对当前挂载点进行卸载删除
count=0
dir="/tmp/mnt/ecryptfs$count"
gksudo df 1>/tmp/1.txt
while cat /tmp/1.txt |grep -q $dir 2>/dev/null ##检查现在存在的挂载
do
echo $dir
let count=$count+1
sudo umount $dir && rmdir $dir ##卸载挂载点,卸载成功的前提下删除挂载点
dir="/tmp/mnt/ecryptfs$count"
done
4. 添加到鼠标右键菜单的执行脚本
#!/bin/bash
##添加到右键的执行脚本,用来打开QT交互式界面,获取用户的输入信息
/home/tang/ecryptfs/imageconverter
#/bin/ecryptfs_mounted.sh&
5. 在QT调用执行挂载脚本
#!/bin/bash
##在QT中调用该脚本,该脚本的主要功能是执行挂载脚本ecryptfs_mounted.sh
REALFILE=$1
ECRYPTFSPATH="/bin/ecryptfs_mounted.sh $REALFILE"
echo $REALFILE 1>/TMP/2.txt
echo
$ECRYPTFSPATH 1>>/tmp/2.txt
exec
$ECRYPTFSPATH
#exec gnome-terminal -x
$ECRYPTFSPATH
#/bin/ecryptfs_mounted.sh&
二. QT界面程序设计
#include <QtGui>
#include "convertdialog.h"
ConvertDialog::ConvertDialog(QWidget *parent)
: QDialog(parent)
{
setupUi(this); //创建并布局好所有的窗口部件
passwordEdit->setEchoMode (QLineEdit::Password);//用户口令输入框
filepasswordEdit->setEchoMode (QLineEdit::Password);//文件密码输入框
}
void ConvertDialog::on_browseButton_clicked()//选择路径按钮触发
{
QString initialName = sourceFileEdit->text();//加密文件夹路径输入框
if (initialName.isEmpty())
initialName = QDir::homePath();//为空,使用默认路径
QString fileName =
QFileDialog::getExistingDirectory(this, tr("Choose File"),
initialName);//弹出计算机路径选择框,选择路径加密
fileName = QDir::toNativeSeparators(fileName);//加密路径
if (!fileName.isEmpty()) {
sourceFileEdit->setText(fileName);//将路径信息保存起来
// buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
}
}
void ConvertDialog::on_passwordEdit_textEdited(const QString &arg1)//password密码框
{
QRegExp regx ("^[^ ]+$");// 设定正则表达式,不能输入空格
QValidator *validator=new QRegExpValidator(regx,this);
passwordEdit->setValidator(validator);
filepasswordEdit->setValidator(validator);//正则表达式控制输入框字符格式
}
void ConvertDialog::on_filepasswordEdit_textEdited(const QString &arg1)//filepassword密码框
{
QRegExp regx ("^[^ ]+$");// 不能输入空格
QValidator *validator2=new QRegExpValidator(regx,this);
passwordEdit->setValidator(validator2);
filepasswordEdit->setValidator(validator2);//正则表达式控制输入框字符格式
}
void ConvertDialog::on_shellButton_clicked()//确定加密按钮触发
{
//system("/bin/ecryptfs_mounted.sh /home/tang/"); //直接调用的方式,会阻塞进程
//QProcess::execute("/opt/run") //调用QT里面的函数来实现,会阻塞进程
passwordEdit->setInputMask("");//提取password输入框内容
filepasswordEdit->setInputMask("");//提取filepassword输入框内容
if(sourceFileEdit->text().isEmpty()) // 必须输入加密路径,否则报错
{
QMessageBox::warning(this,tr("worning"),tr(" Please select the path to encrypt !"),QMessageBox::Yes);//报错信息
sourceFileEdit->setFocus();//移动光标到加密路径输入框
}
else if(passwordEdit->text().isEmpty()||filepasswordEdit->text().isEmpty())//如果任一密码为空,直接报错
{
QMessageBox::warning(this,tr("worning"),tr(" user password or file password can't be empty!"),QMessageBox::Yes);
//报错信息
passwordEdit->clear();//清空用户密码输入框
filepasswordEdit->clear();//清空文件密码输入框
passwordEdit->setFocus();//移动光标到用户密码输入框
}
else
{
QString p1=passwordEdit->text().trimmed();//去除首尾空格
QString f1=filepasswordEdit->text().trimmed();// 去除首尾空格
QString p2,f2;
int length1=p1.length();
int length2=f1.length();
for (int i=0;i<length1;i++)
{if (p1[i]!=' ') p2+=p1[i];}//去除password中的空格
for (int i=0;i<length2;i++)
{if (f1[i]!=' ') f2+=f1[i];}//去除filepassword中的空格
QString define = p2+" "+"1 "+f2+" "+ecryptfstypeComboBox->currentText().toLower()
+" "+ecryptfsbitComboBox->currentText().toLower()
+" "+fileecryptfsComboBox->currentText().toLower()
+" "+filenameecryptfsComboBox->currentText().toLower();//输出需要的信息
qDebug()<<define;
QString ecryptfs_sh = sourceFileEdit->text()+" "+define;//写出保存的信息
//ecryptfs_sh.insert(0,QString("/bin/tangguoze "));
qDebug()<<ecryptfs_sh;
QStringList arguments;
arguments << ecryptfs_sh;//输出信息到脚本中
QProcess *poc = new QProcess;//定义新进程
poc -> start ("/bin/tangguoze",arguments);//在QT中调用启动进程运行
}
}
三.eCryptfs开源部分的分析
主要分为7个部分:
keystore
Keystore部件从文件中提供文件头信息,并将信息数据转发给callout应用程序。Keystore与callout应用程序之前使用netlink机制通信,通信的发起者为keystore。Callout应用程序
Callout应用程序根据目标策略对头文件信息作出评估,并给出各种操作,如:调用后台给应用程序弹出对话框要求密码短语,或用私钥解密一个会话秘钥。
eCryptfs内核模块和用户空间秘钥管理代码之间的主要通信是请求秘钥,由内核秘钥环发起。Callout应用程序从目录分析策略信息,解析每个文件的头信息。为了满足挂起的公钥请求,他可以调用PKI API,或者用特殊的签名搜索带盐值得密码短语。eCryptfs后台弹出对话框输入密码短语
为了能给用户弹出对话框来输入密码短语,eCryptfs必须提供得到X会话的通道。这可能通过运行一个后台来实现。eCryptfs后台侦听一个socket,它的地址信息写在用户的会话秘钥环中,无论何时策略需要弹出一个对话框请求密码短语时,callout应用程序都能提取socket的地址信息,并用它请求后台给用户弹出对话框,接着,后台将用户的密码短语返回给callout应用程序。内核秘钥环
内核秘钥环用于管理和保护秘钥和认证特征。eCryptfs用内核秘钥环存储认证特征、节点加密统计信息和秘钥。PAM
PAM(可插入的认证模块)提供了灵活的认证机制。eCryptfs含有一个模块,能捕获用户注册的密码短语,并将它放在用户的会话秘钥环中,这个密码短语作为无盐值密码短语认证特征。 eCryptfs可以基于策略,使用这个密码短语进行加密操作。如:用这个密码短语从用户的GunPG秘钥环中提取他的私钥;通过字符串到秘钥操作,将这个密码短语直接用于保护文件的会话秘钥;这个秘钥短语还可以与存在TPM中的秘钥结合在一起,用来提供两个因子的认证,即用户为了访问一个文件,他必须注册到特殊主机,还需要使用特征的密码短语。公钥设施
eCryptfs提供了可插入的PKI(公钥设施)接口,eCryptfs的PKI模块利用GPGME(GuuPG Made Easy)接口访问用户的GnuPG秘钥环。这个模块能使用用户的注册密码解密用户保存的私钥。
eCryptfs的TMP PKI模块的TrouSerS使用接口与TPM(可信平台模块)通信,用来使用存在硬件中的私钥,将文件绑定到一个特定的主机上。 eCryptfs openCryptoki PKCS#11框架PKI通过各种open Crytok的硬件设备,对在硬件上执行公钥操作的机制提供了支持。目标中心策略(Target-centric Polocies)
当应用程序创建一个新文件时,eCryptfs必须作出许多的决策,如:文件是否加密?用哪个对称密码加密数据?文件是否加入HMAC并附加IV?会话秘钥长度是多少?如何保护会话秘钥?等等。 eCryptfs将策略定义文件应用于目标。
eCryptfs文件系统由内核空间系统和用户空间系统两部分组成。内核空间系统由内核空间的内核keystore、内核加密API、eCryptfs层、加密元数据和底层文件系统组成。而用户空间由callout应用程序,eCryptfs后台和PKI API等组成。
另外,eCryptfs文件系统使用了Linux内核的密钥环服务、Linux可插入认证模块(Pluggable Authentication Modules, PAM)、可信平台模块(Trusten Platform Module, TPM)和GnuPG密钥环,Ecryptfs超级块私有数据主要包括加密的各种信息,如:认证特征、密钥环等。
/* superblock private data. */ struct ecryptfs_sb_info { struct super_block *wsi_sb; struct ecryptfs_mount_crypt_stat mount_crypt_stat; }; /* dentry private data. Each dentry must keep track of a lower * vfsmount too. */ struct ecryptfs_dentry_info { struct path lower_path; struct ecryptfs_crypt_stat *crypt_stat; }; /* inode private data. */ struct ecryptfs_inode_info { struct inode vfs_inode; struct inode *wii_inode; struct file *lower_file; struct mutex lower_file_mutex; struct ecryptfs_crypt_stat crypt_stat; }; /* file private data. */ struct ecryptfs_file_info { struct file *wfi_file; struct ecryptfs_crypt_stat *crypt_stat; }; eCryptfs的认证特征包括会话密钥、口令和私钥等以及他们的签名。会话密钥将口令进行加密。结构eCryptfs_auth_toke存放了eCryptfs文件系统范围内的的认证特征。 /* May be a password or a private key */ struct ecryptfs_auth_tok { u16 version; /* 8-bit major and 8-bit minor */ u16 token_type; #define ECRYPTFS_ENCRYPT_ONLY 0x00000001 u32 flags; struct ecryptfs_session_key session_key; u8 reserved[32]; union { struct ecryptfs_password password; struct ecryptfs_private_key private_key; } token; } __attribute__ ((packed)); 加密统计信息结构ecryptfs_crypt_stat存入了与每个加密文件相关的加密信息。如:文件的加密操作标识、文件头的结构信息等。列出如下: /** * This is the primary struct associated with each encrypted file. * * TODO: cache align/pack? */ struct ecryptfs_crypt_stat { #define ECRYPTFS_STRUCT_INITIALIZED 0x00000001 #define ECRYPTFS_POLICY_APPLIED 0x00000002 #define ECRYPTFS_NEW_FILE 0x00000004 #define ECRYPTFS_ENCRYPTED 0x00000008 #define ECRYPTFS_SECURITY_WARNING 0x00000010 #define ECRYPTFS_ENABLE_HMAC 0x00000020 #define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040 #define ECRYPTFS_KEY_VALID 0x00000080 #define ECRYPTFS_METADATA_IN_XATTR 0x00000100 #define ECRYPTFS_VIEW_AS_ENCRYPTED 0x00000200 #define ECRYPTFS_KEY_SET 0x00000400 u32 flags; unsigned int file_version; size_t iv_bytes; size_t num_header_bytes_at_front; size_t extent_size; /* Data extent size; default is 4096 */ size_t key_size; size_t extent_shift; unsigned int extent_mask; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; struct crypto_blkcipher *tfm; struct crypto_hash *hash_tfm; /* Crypto context for generating * the initialization vectors */ unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE]; unsigned char key[ECRYPTFS_MAX_KEY_BYTES]; unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES]; struct list_head keysig_list; struct mutex keysig_list_mutex; struct mutex cs_tfm_mutex; struct mutex cs_hash_tfm_mutex; struct mutex cs_mutex; };