/* * sdp_ioctl.c * */ #include #include "../fscrypt_private.h" int fscrypt_sdp_ioctl_get_sdp_info(struct inode *inode, unsigned long arg) { struct dek_arg_sdp_info req; struct fscrypt_info *ci; struct ext_fscrypt_info *ext_ci; int result = 0; if (inode->i_crypt_info == NULL) { DEK_LOGE("No encryption context to the target..\n"); return -EOPNOTSUPP; } memset(&req, 0, sizeof(struct dek_arg_sdp_info)); req.engine_id = -1; req.type = -1; req.sdp_enabled = 1; ci = inode->i_crypt_info; ext_ci = GET_EXT_CI(ci); if (!ext_ci->ci_sdp_info) { DEK_LOGE("get_info: can't find sdp info\n"); } else { DEK_LOGD("get_info: ci->i_crypt_info->sdp_flags: 0x%08x\n", ext_ci->ci_sdp_info->sdp_flags); if (ext_ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE) { req.is_sensitive = 1; req.engine_id = ext_ci->ci_sdp_info->engine_id; req.type = ext_ci->ci_sdp_info->sdp_dek.type; } if (ext_ci->ci_sdp_info->sdp_flags & SDP_IS_CHAMBER_DIR) req.is_chamber = 1; } if (copy_to_user((void __user *)arg, &req, sizeof(req))) { DEK_LOGE("get_info: failed to copy data to user\n"); result = -EFAULT; } return result; } int fscrypt_sdp_ioctl_set_sdp_policy(struct inode *inode, unsigned long arg) { dek_arg_set_sdp_policy_t req; int rc = 0; if (inode->i_crypt_info == NULL) { DEK_LOGE("no encryption context to the target..\n"); rc = -EOPNOTSUPP; } else { if (!is_root()) { DEK_LOGE("set_policy: operation not permitted to non-root process\n"); return -EPERM; } memset(&req, 0, sizeof(dek_arg_set_sdp_policy_t)); if (copy_from_user(&req, (dek_arg_set_sdp_policy_t __user *)arg, sizeof(req))) { DEK_LOGE("set_policy: failed to copy data from user\n"); return -EFAULT; } rc = fscrypt_sdp_set_sdp_policy(inode, req.engine_id); if (rc) { DEK_LOGE("set_policy: operation failed (err:%d)\n", rc); rc = -EFAULT; } } return rc; } int fscrypt_sdp_ioctl_set_sensitive(struct inode *inode, unsigned long arg) { dek_arg_set_sensitive_t req; int result = 0; if (inode->i_crypt_info == NULL) { DEK_LOGE("No encryption context to the target..\n"); result = -EOPNOTSUPP; } else { struct fscrypt_info *ci = inode->i_crypt_info; struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci); if (ext_ci->ci_sdp_info && (ext_ci->ci_sdp_info->sdp_flags & SDP_DEK_IS_SENSITIVE)) { DEK_LOGE("already sensitive file\n"); return 0; } if (S_ISDIR(inode->i_mode) && !is_root()) { #ifdef CONFIG_SDP_KEY_DUMP if (get_sdp_sysfs_key_dump()) { DEK_LOGD("Temporarily allowed to process not vold."); } else { #endif DEK_LOGE("Only vold as root process can set sensitive directory\n"); return -EPERM; #ifdef CONFIG_SDP_KEY_DUMP } #endif } memset(&req, 0, sizeof(dek_arg_set_sensitive_t)); if (copy_from_user(&req, (dek_arg_set_sensitive_t __user *)arg, sizeof(req))) { DEK_LOGE("can't copy from user\n"); memset(&req, 0, sizeof(dek_arg_set_sensitive_t)); result = -EFAULT; } else { int rc = fscrypt_sdp_set_sensitive(inode, req.engine_id, NULL); if (rc) { DEK_LOGE("failed to set sensitive rc(%d)\n", rc); memset(&req, 0, sizeof(dek_arg_set_sensitive_t)); return -EFAULT; } memset(&req, 0, sizeof(dek_arg_set_sensitive_t)); } } return result; } int fscrypt_sdp_ioctl_set_protected(struct inode *inode, unsigned long arg) { dek_arg_set_protected_t req; int result = 0; if (inode->i_crypt_info == NULL) { DEK_LOGE("No encryption context to the target..\n"); result = -EOPNOTSUPP; } else { int rc; if (S_ISDIR(inode->i_mode) && !is_root()) { #ifdef CONFIG_SDP_KEY_DUMP if (get_sdp_sysfs_key_dump()) { DEK_LOGD("Temporarily allowed to process not vold."); } else { #endif DEK_LOGE("Only vold as root process can set protected directory\n"); return -EPERM; #ifdef CONFIG_SDP_KEY_DUMP } #endif } memset(&req, 0, sizeof(dek_arg_set_protected_t)); if (copy_from_user(&req, (dek_arg_set_protected_t __user *)arg, sizeof(req))) { DEK_LOGE("set_protected: failed to copy data from user\n"); return -EFAULT; } rc = fscrypt_sdp_set_protected(inode, req.engine_id); if (rc) { DEK_LOGE("failed to set protected rc(%d)\n", rc); result = -EFAULT; } } return result; } int fscrypt_sdp_ioctl_add_chamber_directory(struct inode *inode, unsigned long arg) { int result = 0; if (inode->i_crypt_info == NULL) { DEK_LOGE("No encryption context to the target..\n"); result = -EOPNOTSUPP; } else { dek_arg_add_chamber_t req; struct fscrypt_info *ci = inode->i_crypt_info; struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci); if (!S_ISDIR(inode->i_mode)) { DEK_LOGE("Not directory\n"); return -EOPNOTSUPP; } if (ext_ci->ci_sdp_info && ext_ci->ci_sdp_info->sdp_flags & SDP_IS_CHAMBER_DIR) { DEK_LOGE("Already chamber directory\n"); return 0; } if (!is_root()) { DEK_LOGE("Permission denied: only epm process can call this\n"); return -EPERM; } memset(&req, 0, sizeof(dek_arg_add_chamber_t)); if (copy_from_user(&req, (dek_arg_add_chamber_t __user *)arg, sizeof(req))) { DEK_LOGE("can't copy from user\n"); memset(&req, 0, sizeof(dek_arg_add_chamber_t)); result = -EFAULT; } else { int rc = fscrypt_sdp_add_chamber_directory(req.engine_id, inode); if (rc) { DEK_LOGE("failed to add chamber rc(%d)\n", rc); memset(&req, 0, sizeof(dek_arg_add_chamber_t)); return -EFAULT; } memset(&req, 0, sizeof(dek_arg_add_chamber_t)); } } return result; } int fscrypt_sdp_ioctl_remove_chamber_directory(struct inode *inode) { int result = 0; if (inode->i_crypt_info == NULL) { DEK_LOGE("No encryption context to the target..\n"); result = -EOPNOTSUPP; } else { int rc; struct fscrypt_info *ci = inode->i_crypt_info; struct ext_fscrypt_info *ext_ci = GET_EXT_CI(ci); if (!ext_ci->ci_sdp_info || !(ext_ci->ci_sdp_info->sdp_flags & SDP_IS_CHAMBER_DIR)) { DEK_LOGE("Not chamber directory\n"); return 0; } if (!is_root()) { DEK_LOGE("Permission denied: only epm process can call this\n"); return -EPERM; } rc = fscrypt_sdp_remove_chamber_directory(inode); if (rc) { DEK_LOGE("failed to remove chamber rc(%d)\n", rc); result = -EFAULT; } } return result; } #ifdef CONFIG_SDP_KEY_DUMP int fscrypt_sdp_ioctl_dump_file_key(struct inode *inode) { int rc = 0; DEK_LOGE("%s(ino:%ld)\n", __func__, inode->i_ino); if (inode->i_crypt_info == NULL) { DEK_LOGE("no encryption context to the target..\n"); rc = -EOPNOTSUPP; } else { rc = fscrypt_sdp_dump_file_key(inode); if (rc) { DEK_LOGE("dump_file_key: operation failed (err:%d)\n", rc); rc = -EFAULT; } } return rc; } int fscrypt_sdp_ioctl_trace_file(struct inode *inode) { int rc = 0; DEK_LOGI("%s(ino:%ld)\n", __func__, inode->i_ino); if (inode->i_crypt_info == NULL) { DEK_LOGE("no encryption context to the target..\n"); rc = -EOPNOTSUPP; } else { rc = fscrypt_sdp_trace_file(inode); if (rc) { DEK_LOGE("trace_file: operation failed (err:%d)\n", rc); rc = -EFAULT; } } return rc; } #endif // End of CONFIG_SDP_KEY_DUMP /* * -ENOTTY will be returned if this ioctl is not related to SDP */ int fscrypt_sdp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); if (!fscrypt_has_encryption_key(inode)) { if (fscrypt_prepare_readdir(inode)) { //Not allowed without i_crypt_info, go to the default ioctl return -ENOTTY; } } switch (cmd) { case FS_IOC_GET_SDP_INFO: return fscrypt_sdp_ioctl_get_sdp_info(inode, arg); case FS_IOC_SET_SDP_POLICY: return fscrypt_sdp_ioctl_set_sdp_policy(inode, arg); case FS_IOC_SET_SENSITIVE: return fscrypt_sdp_ioctl_set_sensitive(inode, arg); case FS_IOC_SET_PROTECTED: return fscrypt_sdp_ioctl_set_protected(inode, arg); case FS_IOC_ADD_CHAMBER: return fscrypt_sdp_ioctl_add_chamber_directory(inode, arg); case FS_IOC_REMOVE_CHAMBER: return fscrypt_sdp_ioctl_remove_chamber_directory(inode); #ifdef CONFIG_SDP_KEY_DUMP case FS_IOC_DUMP_FILE_KEY: return fscrypt_sdp_ioctl_dump_file_key(inode); case FS_IOC_TRACE_FILE: return fscrypt_sdp_ioctl_trace_file(inode); #endif default: return -ENOTTY; } }