透明加密一直是安全领域比较热门的领域,了解了下linux内核自带的ecryptfs。首先作者为了实现透明加密,构造了一个ecryptfs的文件系统。这个文件系统并没有对, 磁盘文件的直接组织,而是依附在已有的文件系统上,通过构建的文件系统对已有的文件系统做了重新组织实现。
正如常见的文件系统构成一样,ecrypt构建了自己的文件系统节点管理逻辑。
之后是磁盘管理和虚拟文件系统管理。这些都是依附与一个叫lowe_path的核心逻辑,就是未加密文件夹。通过对未加密文件夹重新组织,实现对未加密文件页缓存加密。
为了构造一个完整的加密文件系统,不可能自己实现inode dentry的管理,因为需要劫持底层ext4等文件的页缓存。那么就要依赖现有的文件inode管理。
研究struct file_system_type 的mount实现,可以观察到:
整个文件系统围绕 dev_name构造的lower_path lower_sb进行。这个节点就是真实文件系统的节点管理的核心,也就是加密前的文件系统。这样借助现有文件系统,可以更方便的虚拟处一个文件系统,但实际数据全部还是在真是的文件系统上。
static struct inode *
ecryptfs_do_create(struct inode *directory_inode,
struct dentry *ecryptfs_dentry, umode_t mode)
{
int rc;
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
struct inode *inode;
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
rc = vfs_create(d_inode(lower_dir_dentry), lower_dentry, mode, true);
if (rc) {
printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
"rc = [%d]\n", __func__, rc);
inode = ERR_PTR(rc);
goto out_lock;
}
inode = __ecryptfs_get_inode(d_inode(lower_dentry),
directory_inode->i_sb);
if (IS_ERR(inode)) {
vfs_unlink(d_inode(lower_dir_dentry), lower_dentry, NULL);
goto out_lock;
}
fsstack_copy_attr_times(directory_inode, d_inode(lower_dir_dentry));
fsstack_copy_inode_size(directory_inode, d_inode(lower_dir_dentry));
out_lock:
unlock_dir(lower_dir_dentry);
return inode;
}
这里跟我猜测的一样,利用vfs_create触发了底层文件系统节点构造方法。
可以看到直接利用 d_instantiate_new(ecryptfs_dentry, ecryptfs_inode); 把底层文件系统的几点元数据关联到了ecryptfs文件系统。表现就是,挂载点可以看到被挂在点的全部数据。
static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
struct dentry *ecryptfs_dentry,
unsigned int flags)
{
char *encrypted_and_encoded_name = NULL;
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct dentry *lower_dir_dentry, *lower_dentry;
const char *name = ecryptfs_dentry->d_name.name;
size_t len = ecryptfs_dentry->d_name.len;
struct dentry *res;
int rc = 0;
lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent);
mount_crypt_stat = &ecryptfs_superblock_to_private(
ecryptfs_dentry->d_sb)->mount_crypt_stat;
if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
rc = ecryptfs_encrypt_and_encode_filename(
&encrypted_and_encoded_name, &len,
mount_crypt_stat, name, len);
if (rc) {
printk(KERN_ERR "%s: Error attempting to encrypt and encode "
"filename; rc = [%d]\n", __func__, rc);
return ERR_PTR(rc);
}
name = encrypted_and_encoded_name;
}
lower_dentry = lookup_one_len_unlocked(name, lower_dir_dentry, len);
if (IS_ERR(lower_dentry)) {
ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
"[%ld] on lower_dentry = [%s]\n", __func__,
PTR_ERR(lower_dentry),
name);
res = ERR_CAST(lower_dentry);
} else {
res = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry);
}
kfree(encrypted_and_encoded_name);
return res;
}
这里是加解密的核心,如何借助现有文件系统,实现加解密的逻辑都在这里,其实就是把原有文件系统的页加密。
static int ecryptfs_readpage(struct file *file, struct page *page)
{
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(page->mapping->host)->crypt_stat;
int rc = 0;
if (!crypt_stat || !(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
rc = ecryptfs_read_lower_page_segment(page, page->index, 0,
PAGE_SIZE,
page->mapping->host);
} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs,
pgoff_t page_index,
size_t offset_in_page, size_t size,
struct inode *ecryptfs_inode)
{
char *virt;
loff_t offset;
int rc;
offset = ((((loff_t)page_index) << PAGE_SHIFT) + offset_in_page);
virt = kmap(page_for_ecryptfs);
rc = ecryptfs_read_lower(virt, offset, size, ecryptfs_inode);
if (rc > 0)
rc = 0;
kunmap(page_for_ecryptfs);
flush_dcache_page(page_for_ecryptfs);
return rc;
}
int ecryptfs_read_lower(char *data, loff_t offset, size_t size,
struct inode *ecryptfs_inode)
{
struct file *lower_file;
lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file;
if (!lower_file)
return -EIO;
return kernel_read(lower_file, data, size, &offset);
}
最终借助前期关联的源文件struct file,读取文件。