李春杰+张启军+谭嘉瑞+颜智润
摘 要:对文件内容加密,可有效解决数据泄露问题。目前传统的第三方软件多采用非透明式加密,对用户而言,操作不便;新型的堆叠式文件加密方案可以有效解决透明性问题,但此类设计方案实施较为复杂,并存在内核版本依赖性。文中采用Hook机制,通过监控用户对指定文件目录进行操作,捕获用户对该目录具体文件的访问;自动调用应用层的加解密模块,完成对具体文件的加解密,解决透明性问题。由于该系统的加解密功能在应用层实现,与具体文件系统无关,因此又可以有效简化系统设计,灾难恢复性强,稳定性高。
关键词:数据加密;进程间约束;消息Hook机制;锁机制
中图分类号:TP309.7;TP316.81 文献标识码:A 文章编号:2095-1302(2018)02-00-03
0 引 言
最近几年,基于Linux内核开发的操作系统逐渐从服务端走向用户终端。尤其某些单位,从服务器到个人终端都以Linux作为系统支撑。因此,对于存放在Linux上的敏感数据进行加密保护,已成为一个急需解决的问题。
多数第三方加密软件均采用非透明式文件存储方式,需要用户在打开指定文件时调用解密模块;同样,当用户关闭文件时,需要调用加密模块,导致体验效果不好。
新型堆叠式文件加密系统[1]虽然可以解决用户透明性问题,但需要重新设计一个新的文件管理层,用以对文件加密,因此需要对加密文件系统进行重新設计。此类方案对于设计者而言,需要深入掌握Linux各方面的知识,如文件系统管理、内存管理、驱动设计等。虽然设计方案在理论上可行,但在系统的具体实现过程中却十分复杂,而且开发周期长,成本昂贵,短期内看不出测试结果。
1 整体方案设计
该系统使用文件锁[2]和消息Hook机制[3]进行设计,既可以有效解决用户透明性问题,又可以降低整体设计难度。如图1所示,系统中目录A作为被监控目录,用于存放需要加密的文件,目录B可以视为缓存目录或者备份目录。当明文文件被存放到目录A中时,加密子进程在目录B中创建一个新的临时文件,用以保存加密之后的密文。当明文文件加密完成后,再利用目录B中的临时密文文件替换目录A中的文件。同样,当需要对目录A中的密文文件解密时,读取目录B中的临时密文文件,然后调用解密子进程对其解密,最后将解密之后的数据覆盖目录A的密文文件。
整体方案只需调用Linux提供的接口就可以完成设计,且绝大部分数据处理过程都位于应用层。即便系统遇到不可控因素而产生灾难性后果,也可以在现有文件系统中找到保存完整的加密数据,对其进行解密操作,有效解决文件恢复问题。可以看出,基于应用层的方案设计无须对Linux内核进行深层次的修改,降低了开发难度。
2 关键技术点
目录A和目录B间的数据交换需要多个子进程先后进行协调工作,包括维护子进程、加密子进程、解密子进程。如果用户操作目录A以及目录A中的文件,那么维护子进程就能够捕捉到用户的操作,并且通知其他子进程进行后续操作。
系统方案的设计需要解决两个技术点,即监控目录和进程间控制。
2.1 监控目录
在Linux内核最新版本中添加了监控操作文件的消息Hook模块,即inotify,利用inotify作为实现监控目录的核心。
监控模块经过初始化后,返回相应的监控实例id,然后将需要监控的对象路径添加到监控实例中,这些对象可以是文件,也可以是文件目录。若不监控该对象,则可以从监控实例中移除。
监控用户操作文件并产生消息队列的过程如图2所示。当用户对监控目录A中的文件进行操作时,消息Hook模块便对虚拟文件系统(VFS)进行监控,将用户的操作类型通过消息方式发送到应用层的消息队列中,使得后台维护子进程(通过轮询方式)能够实时捕捉。
维护子进程通过读取消息队列[4]判断产生的事件并调用对应的加密或解密子进程。通常用户在访问一个文件时,可能会产生连续多个消息事件。例如,用户打开一个加密文件,监控模块首先产生IN_OPEN消息事件,然后产生IN_WRITE消息事件,最后产生IN_CLOSE消息事件。因此,在监控一个对象时,需要维护子进程依次捕获该对象所产生的一系列消息事件,判断当前监控对象需要进行的操作。
2.2 进程间控制
用户使用编辑器等软件打开目录A中的密文文件,维护子进程自动调用加解密模块完成文件解密过程,将解密之后的明文数据呈现给用户。在解密过程中,采用文件锁方式使用户进程产生等待,便于解密模块解密。
但多数编辑软件在打开文件时不检查文件锁,而是直接读取文件内容,因此用户读取的是密文数据。为了使这些软件产生等待,在文件打开时,需要将这些软件产生的进程强制性地检查文件是否被锁定。由于这些进程打开的文件最终都需要调用内核层中的open函数,所以需要修改Linux内核中系统定义的open部分[5]。
在Linux内核中需要添加文件锁检测函数,命名为myFlock。由于内核中Flock的系统定义只能提供给上层进行系统调用而不能直接被内核层的其他函数调用,所以需要在do_sys_open中调用自定义的锁。由于内核中的函数都通过EXPORT_SYMBOL等方式输出全局信号量[6],因此,Flock和myFlock可以通过调用全局输出的信号量来共享缓存队列,从而使得其他进程在打开文件时强制检查锁。此外,内核层中进程之间的约束过程如下:
(1)在控制进程中,密文文件被维护子进程调用flock(),维护子进程持有加密文件锁;
(2)将与密文文件相关的信息加入缓存队列中;
(3)其他进程打开文件需要调用open()函数,在do_sys_open()返回文件描述符之前[7],需要先调用myFlock()自定义锁;endprint
(4)若该文件是被监控文件,则其他进程的访问需要等待文件锁释放;
(5)do_sys_open()将通过系统调用open()返回文件描述符给其他进程;
(6)其他进程读取文件。
文中详述了利用文件锁方式进行进程间控制的过程,使得多个并发进程能够有序调度。如果打开的对象不是被监控文件,那么其他进程在内核层中获取锁之后应该立即释放文件锁,避免整个系统被锁死。
3 模块方案设计
虽然系统中有多个子进程先后协调工作,但为了降低方案设计的复杂度,可以将系统分为加密过程和解密过程[8]两部分。
3.1 读解密操作
对于用户而言,监控目录下存放的文件虽然以密文数据的形式存放在磁盘中,但当用户访问文件时,文件应当以明文形式呈现给用户。
如图3所示,当用户打开一份密文文件时,需要先让用户等待。此时的监控模块产生IN_OPEN消息并插入到消息队列中,然后维护子进程捕获该消息,调用解密子过程直到解密完成后,返回维护子进程使得维护子进程释放目录A下的文件锁,用户再读取解密文件时便可以获得明文。系统的解密过程如下:
(1)维护进程初始化时会扫描目录A下的文件及目录,并把这些目录和文件的绝对路径放入到已加密文件的监控链表中,同时给文件添加文件锁(系统默认目录A中已存在的文件为密文或目录为空);
(2)用户打开目录A中的加密文件2,监控模块产生IN_OPEN消息事件,由于文件锁的存在,使得用户进程产生等待;
(3)维护子进程通过不断轮询捕获到该消息并查找已加密文件监控链表,判断该消息所代表的文件是否为密文;
(4)复制目录A中的加密文件2到目录B中;
(5)维护子进程调用解密程序,解密程序读取目录B中的加密文件2并解密,将解密之后的数据写入目录A中的解密文件2中;
(6)解密完成,解密程序将加密文件2从已加密文件监控链表中移除,并将其添加到待加密的文件链表(在监控目录A中以明文形式存在的文件)中;
(7)维护子进程删除目录B中的加密文件2并释放目录A中解密文件2的文件锁;
(8)用户读取解密文件2。
需要注意的是待加密的文件链表中存放的是目录A中未加密的明文文件节点,当用户读取文件数据或者其他操作结束之后便关闭该文件并产生IN_CLOSE消息事件,此时就需要维护线程通过读取待加密的文件链表,重新对该目录A中的明文文件进行加密处理。解密过程如图4所示。
3.2 写加密操作
存在于目录A下的明文文件可以来源于从其他目录转移到目录A中的新明文文件,或者用户读取目录A中的密文文件时通过读解密操作之后留下的明文文件。针对第二种情况,在上述解密操作过程中,后台子进程已自动将解密文件相关信息添加到了待加密文件链表中,当IN_CLOSE消息到来时就可以通过加密程序对明文文件进行加密操作。然而,对于第一种情况,监控模块也会产生对应的IN_OPEN以及IN_CLOSE消息。因此,只要将监控目录中的新文件相关信息加入到待加密文件链表中,IN_CLOSE消息到来时就可通过加密程序对新明文文件进行加密操作。如图5所示,系统的加密过程如下:
(1)访问监控目录A并产生相应消息事件;
(2)如果是一份新明文文件N,那么监控模块将依次产生IN_OPEN和IN_CLOSE等多个重要的消息;
(3)维护进程首先扫描消息链表,得到IN_OPEN消息;
(4)将明文文件N的节点添加到待加密的文件链表中;
(5)维护子进程继续扫描消息队列;
(6)维护子进程扫描IN_CLOSE消息,在待加密文件链表里找到该消息代表的文件;
(7)调用加密程序对文件进行加密;
(8)加密程序完成后,将该文件节点从待加密文件链表中移除,并给该文件上锁。
目录A中的文件既存有明文文件,又存有密文文件。在写加密操作中,一些待加密的文件都通过待加密文件链表维护。
4 测试结果
文件加密系统初始化運行,指定目录A的绝对路径。首先读取路径下的所有文件及子目录。然后对整个目录A的文件加密。最后,强行中止文件加密系统运行。如图6所示,打开该目录下的文件,显示的是用户无法阅读的乱码,如文本(txt),图片(jpg)。
关闭上述所打开的文件,再重新启动文件加密系统。因为已经运行过一次,所以需要读取日志文件[9],但发现所有文件都已被加密。
再打开上述文件,此时后台维护子进程启动,调用解密子进程完成文件解密过程,释放文件锁。如图7所示,用户读取的文本(txt)为明文,图片(jpg)无格式错误,能够进行正常预览。
5 结 语
本文主要采用文件锁和消息Hook机制进行系统设计,因此需要多个子进程之间的协调工作。基于应用层的设计,降低了整个系统的实现复杂度。同时,兼容了各种文件系统格式,通过对监控目录的备份保证了数据的可恢复性。解密之后的文件可以对文件内容进行Hash校验[10],从而确定解密之后的文件是否与原始文件的内容相同。
参考文献
[1]刑常亮,卿斯汉,李丽萍.一个基于Linux的加密文件系统的设计与实现[J].计算机工程与应用,2005,41(17):101-104.
[2] Randal E Bryant , David OHallaron.Computer Systems: A Programmers Perspective[M].北京:机械工业出版社,2011.
[3]刘刚,赵剑川. Linux系统移植[M].北京:清华大学出版社,2011.
[4]邱铁,周玉,邓莹莹.Linux内核API完全参考手册[M].北京:机械工业出版社,2011.
[5] Wolfgang Mauerer. Professional Linux Kernel Architecture[M]. 北京:人民邮电出版社,2008.
[6] Neil Matthew,Richard Stones. Beginning Linux Programming [M].北京:人民邮电出版社,2000.
[7]庞丽萍,郑然.操作系统原理与Linux系统实验[M].北京:机械工业出版社,2011.
[8]解双建,原亮,郝琳,等.一种有效的RSA算法改进方案[J].计算机应用,2010,30(9):2293-2397.
[9]刘斌.基于Linux的文件实时备份系统设计与实现[D].保定:华北电力大学,2014.
[10]徐术坤.Hash算法的研究及应用[D].武汉:湖北工业大学,2006.endprint