📄 crypto.c
字号:
#define ECRYPTFS_DONT_VALIDATE_HEADER_SIZE 0#define ECRYPTFS_VALIDATE_HEADER_SIZE 1static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat, char *virt, int *bytes_read, int validate_header_size){ int rc = 0; u32 header_extent_size; u16 num_header_extents_at_front; memcpy(&header_extent_size, virt, sizeof(u32)); header_extent_size = be32_to_cpu(header_extent_size); virt += sizeof(u32); memcpy(&num_header_extents_at_front, virt, sizeof(u16)); num_header_extents_at_front = be16_to_cpu(num_header_extents_at_front); crypt_stat->num_header_extents_at_front = (int)num_header_extents_at_front; (*bytes_read) = (sizeof(u32) + sizeof(u16)); if ((validate_header_size == ECRYPTFS_VALIDATE_HEADER_SIZE) && ((crypt_stat->extent_size * crypt_stat->num_header_extents_at_front) < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE)) { rc = -EINVAL; printk(KERN_WARNING "Invalid number of header extents: [%zd]\n", crypt_stat->num_header_extents_at_front); } return rc;}/** * set_default_header_data * @crypt_stat: The cryptographic context * * For version 0 file format; this function is only for backwards * compatibility for files created with the prior versions of * eCryptfs. */static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat){ crypt_stat->num_header_extents_at_front = 2;}/** * ecryptfs_read_headers_virt * @page_virt: The virtual address into which to read the headers * @crypt_stat: The cryptographic context * @ecryptfs_dentry: The eCryptfs dentry * @validate_header_size: Whether to validate the header size while reading * * Read/parse the header data. The header format is detailed in the * comment block for the ecryptfs_write_headers_virt() function. * * Returns zero on success */static int ecryptfs_read_headers_virt(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat, struct dentry *ecryptfs_dentry, int validate_header_size){ int rc = 0; int offset; int bytes_read; ecryptfs_set_default_sizes(crypt_stat); crypt_stat->mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; offset = ECRYPTFS_FILE_SIZE_BYTES; rc = contains_ecryptfs_marker(page_virt + offset); if (rc == 0) { rc = -EINVAL; goto out; } offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset), &bytes_read); if (rc) { ecryptfs_printk(KERN_WARNING, "Error processing flags\n"); goto out; } if (crypt_stat->file_version > ECRYPTFS_SUPPORTED_FILE_VERSION) { ecryptfs_printk(KERN_WARNING, "File version is [%d]; only " "file version [%d] is supported by this " "version of eCryptfs\n", crypt_stat->file_version, ECRYPTFS_SUPPORTED_FILE_VERSION); rc = -EINVAL; goto out; } offset += bytes_read; if (crypt_stat->file_version >= 1) { rc = parse_header_metadata(crypt_stat, (page_virt + offset), &bytes_read, validate_header_size); if (rc) { ecryptfs_printk(KERN_WARNING, "Error reading header " "metadata; rc = [%d]\n", rc); } offset += bytes_read; } else set_default_header_data(crypt_stat); rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset), ecryptfs_dentry);out: return rc;}/** * ecryptfs_read_xattr_region * @page_virt: The vitual address into which to read the xattr data * @ecryptfs_inode: The eCryptfs inode * * Attempts to read the crypto metadata from the extended attribute * region of the lower file. * * Returns zero on success; non-zero on error */int ecryptfs_read_xattr_region(char *page_virt, struct inode *ecryptfs_inode){ struct dentry *lower_dentry = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file->f_dentry; ssize_t size; int rc = 0; size = ecryptfs_getxattr_lower(lower_dentry, ECRYPTFS_XATTR_NAME, page_virt, ECRYPTFS_DEFAULT_EXTENT_SIZE); if (size < 0) { printk(KERN_ERR "Error attempting to read the [%s] " "xattr from the lower file; return value = [%zd]\n", ECRYPTFS_XATTR_NAME, size); rc = -EINVAL; goto out; }out: return rc;}int ecryptfs_read_and_validate_xattr_region(char *page_virt, struct dentry *ecryptfs_dentry){ int rc; rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_dentry->d_inode); if (rc) goto out; if (!contains_ecryptfs_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES)) { printk(KERN_WARNING "Valid data found in [%s] xattr, but " "the marker is invalid\n", ECRYPTFS_XATTR_NAME); rc = -EINVAL; }out: return rc;}/** * ecryptfs_read_metadata * * Common entry point for reading file metadata. From here, we could * retrieve the header information from the header region of the file, * the xattr region of the file, or some other repostory that is * stored separately from the file itself. The current implementation * supports retrieving the metadata information from the file contents * and from the xattr region. * * Returns zero if valid headers found and parsed; non-zero otherwise */int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry){ int rc = 0; char *page_virt = NULL; struct inode *ecryptfs_inode = ecryptfs_dentry->d_inode; struct ecryptfs_crypt_stat *crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat; struct ecryptfs_mount_crypt_stat *mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; ecryptfs_copy_mount_wide_flags_to_inode_flags(crypt_stat, mount_crypt_stat); /* Read the first page from the underlying file */ page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, GFP_USER); if (!page_virt) { rc = -ENOMEM; printk(KERN_ERR "%s: Unable to allocate page_virt\n", __FUNCTION__); goto out; } rc = ecryptfs_read_lower(page_virt, 0, crypt_stat->extent_size, ecryptfs_inode); if (!rc) rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, ecryptfs_dentry, ECRYPTFS_VALIDATE_HEADER_SIZE); if (rc) { rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_inode); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " "file header region or xattr region\n"); rc = -EINVAL; goto out; } rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, ecryptfs_dentry, ECRYPTFS_DONT_VALIDATE_HEADER_SIZE); if (rc) { printk(KERN_DEBUG "Valid eCryptfs headers not found in " "file xattr region either\n"); rc = -EINVAL; } if (crypt_stat->mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) { crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } else { printk(KERN_WARNING "Attempt to access file with " "crypto metadata only in the extended attribute " "region, but eCryptfs was mounted without " "xattr support enabled. eCryptfs will not treat " "this like an encrypted file.\n"); rc = -EINVAL; } }out: if (page_virt) { memset(page_virt, 0, PAGE_CACHE_SIZE); kmem_cache_free(ecryptfs_header_cache_1, page_virt); } return rc;}/** * ecryptfs_encode_filename - converts a plaintext file name to cipher text * @crypt_stat: The crypt_stat struct associated with the file anem to encode * @name: The plaintext name * @length: The length of the plaintext * @encoded_name: The encypted name * * Encrypts and encodes a filename into something that constitutes a * valid filename for a filesystem, with printable characters. * * We assume that we have a properly initialized crypto context, * pointed to by crypt_stat->tfm. * * TODO: Implement filename decoding and decryption here, in place of * memcpy. We are keeping the framework around for now to (1) * facilitate testing of the components needed to implement filename * encryption and (2) to provide a code base from which other * developers in the community can easily implement this feature. * * Returns the length of encoded filename; negative if error */intecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat, const char *name, int length, char **encoded_name){ int error = 0; (*encoded_name) = kmalloc(length + 2, GFP_KERNEL); if (!(*encoded_name)) { error = -ENOMEM; goto out; } /* TODO: Filename encryption is a scheduled feature for a * future version of eCryptfs. This function is here only for * the purpose of providing a framework for other developers * to easily implement filename encryption. Hint: Replace this * memcpy() with a call to encrypt and encode the * filename, the set the length accordingly. */ memcpy((void *)(*encoded_name), (void *)name, length); (*encoded_name)[length] = '\0'; error = length + 1;out: return error;}/** * ecryptfs_decode_filename - converts the cipher text name to plaintext * @crypt_stat: The crypt_stat struct associated with the file * @name: The filename in cipher text * @length: The length of the cipher text name * @decrypted_name: The plaintext name * * Decodes and decrypts the filename. * * We assume that we have a properly initialized crypto context, * pointed to by crypt_stat->tfm. * * TODO: Implement filename decoding and decryption here, in place of * memcpy. We are keeping the framework around for now to (1) * facilitate testing of the components needed to implement filename * encryption and (2) to provide a code base from which other * developers in the community can easily implement this feature. * * Returns the length of decoded filename; negative if error */intecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat, const char *name, int length, char **decrypted_name){ int error = 0; (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL); if (!(*decrypted_name)) { error = -ENOMEM; goto out; } /* TODO: Filename encryption is a scheduled feature for a * future version of eCryptfs. This function is here only for * the purpose of providing a framework for other developers * to easily implement filename encryption. Hint: Replace this * memcpy() with a call to decode and decrypt the * filename, the set the length accordingly. */ memcpy((void *)(*decrypted_name), (void *)name, length); (*decrypted_name)[length + 1] = '\0'; /* Only for convenience * in printing out the * string in debug * messages */ error = length;out: return error;}/** * ecryptfs_process_key_cipher - Perform key cipher initialization. * @key_tfm: Crypto context for key material, set by this function * @cipher_name: Name of the cipher * @key_size: Size of the key in bytes * * Returns zero on success. Any crypto_tfm structs allocated here * should be released by other functions, such as on a superblock put * event, regardless of whether this function succeeds for fails. */static intecryptfs_process_key_cipher(struct crypto_blkcipher **key_tfm, char *cipher_name, size_t *key_size){ char dummy_key[ECRYPTFS_MAX_KEY_BYTES]; char *full_alg_name; int rc; *key_tfm = NULL; if (*key_size > ECRYPTFS_MAX_KEY_BYTES) { rc = -EINVAL; printk(KERN_ERR "Requested key size is [%Zd] bytes; maximum " "allowable is [%d]\n", *key_size, ECRYPTFS_MAX_KEY_BYTES); goto out; } rc = ecryptfs_crypto_api_algify_cipher_name(&full_alg_name, cipher_name, "ecb"); if (rc) goto out; *key_tfm = crypto_alloc_blkcipher(full_alg_name, 0, CRYPTO_ALG_ASYNC); kfree(full_alg_name); if (IS_ERR(*key_tfm)) { rc = PTR_ERR(*key_tfm); printk(KERN_ERR "Unable to allocate crypto cipher with name " "[%s]; rc = [%d]\n", cipher_name, rc); goto out; } crypto_blkcipher_set_flags(*key_tfm, CRYPTO_TFM_REQ_WEAK_KEY); if (*key_size == 0) { struct blkcipher_alg *alg = crypto_blkcipher_alg(*key_tfm); *key_size = alg->max_keysize; } get_random_bytes(dummy_key, *key_size); rc = crypto_blkcipher_setkey(*key_tfm, dummy_key, *key_size); if (rc) { printk(KERN_ERR "Error attempting to set key of size [%Zd] for " "cipher [%s]; rc = [%d]\n", *key_size, cipher_name, rc); rc = -EINVAL; goto out; }out: return rc;}struct kmem_cache *ecryptfs_key_tfm_cache;struct list_head key_tfm_list;struct mutex key_tfm_list_mutex;int ecryptfs_init_crypto(void){ mutex_init(&key_tfm_list_mutex); INIT_LIST_HEAD(&key_tfm_list); return 0;}int ecryptfs_destroy_crypto(void){ struct ecryptfs_key_tfm *key_tfm, *key_tfm_tmp; mutex_lock(&key_tfm_list_mutex); list_for_each_entry_safe(key_tfm, key_tfm_tmp, &key_tfm_list, key_tfm_list) { list_del(&key_tfm->key_tfm_list); if (key_tfm->key_tfm) crypto_free_blkcipher(key_tfm->key_tfm); kmem_cache_free(ecryptfs_key_tfm_cache, key_tfm); } mutex_unlock(&key_tfm_list_mutex); return 0;}intecryptfs_add_new_key_tfm(struct ecryptfs_key_tfm **key_tfm, char *cipher_name, size_t key_size){ struct ecryptfs_key_tfm *tmp_tfm; int rc = 0; tmp_tfm = kmem_cache_alloc(ecryptfs_key_tfm_cache, GFP_KERNEL); if (key_tfm != NULL) (*key_tfm) = tmp_tfm; if (!tmp_tfm) { rc = -ENOMEM; printk(KERN_ERR "Error attempting to allocate from " "ecryptfs_key_tfm_cache\n"); goto out; } mutex_init(&tmp_tfm->key_tfm_mutex); strncpy(tmp_tfm->cipher_name, cipher_name, ECRYPTFS_MAX_CIPHER_NAME_SIZE); tmp_tfm->cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0'; tmp_tfm->key_size = key_size; rc = ecryptfs_process_key_cipher(&tmp_tfm->key_tfm, tmp_tfm->cipher_name, &tmp_tfm->key_size); if (rc) { printk(KERN_ERR "Error attempting to initialize key TFM " "cipher with name = [%s]; rc = [%d]\n", tmp_tfm->cipher_name, rc); kmem_cache_free(ecryptfs_key_tfm_cache, tmp_tfm); if (key_tfm != NULL) (*key_tfm) = NULL; goto out; } mutex_lock(&key_tfm_list_mutex); list_add(&tmp_tfm->key_tfm_list, &key_tfm_list); mutex_unlock(&key_tfm_list_mutex);out: return rc;}int ecryptfs_get_tfm_and_mutex_for_cipher_name(struct crypto_blkcipher **tfm, struct mutex **tfm_mutex, char *cipher_name){ struct ecryptfs_key_tfm *key_tfm; int rc = 0; (*tfm) = NULL; (*tfm_mutex) = NULL; mutex_lock(&key_tfm_list_mutex); list_for_each_entry(key_tfm, &key_tfm_list, key_tfm_list) { if (strcmp(key_tfm->cipher_name, cipher_name) == 0) { (*tfm) = key_tfm->key_tfm; (*tfm_mutex) = &key_tfm->key_tfm_mutex; mutex_unlock(&key_tfm_list_mutex); goto out; } } mutex_unlock(&key_tfm_list_mutex); rc = ecryptfs_add_new_key_tfm(&key_tfm, cipher_name, 0); if (rc) { printk(KERN_ERR "Error adding new key_tfm to list; rc = [%d]\n", rc); goto out; } (*tfm) = key_tfm->key_tfm; (*tfm_mutex) = &key_tfm->key_tfm_mutex;out: return rc;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -