/* * Audit calls for FIVE audit subsystem. * * Copyright (C) 2017 Samsung Electronics, Inc. * Egor Uleyskiy, * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include "five.h" #include "five_audit.h" #include "five_cache.h" #include "five_porting.h" #include "five_dsms.h" #include "five_testing.h" __visible_for_testing __mockable void five_audit_msg(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result); #ifdef CONFIG_FIVE_AUDIT_VERBOSE __mockable void five_audit_verbose(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result) { five_audit_msg(task, file, op, prev, tint, cause, result); } #else __mockable void five_audit_verbose(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result) { } #endif void five_audit_info(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result) { five_audit_msg(task, file, op, prev, tint, cause, result); } __visible_for_testing __mockable void call_five_dsms_reset_integrity(const char *task_name, int result, const char *file_name) { five_dsms_reset_integrity(task_name, result, file_name); } /** * There are two kind of event that can come to the function: error * and tampering attempt. 'result' is for identification of error type * and it should be non-zero in case of error but is always zero in * case of tampering. */ void five_audit_err(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result) { five_audit_msg(task, file, op, prev, tint, cause, result); if (!result) { char comm[TASK_COMM_LEN]; struct task_struct *tsk = task ? task : current; call_five_dsms_reset_integrity(get_task_comm(comm, tsk), 0, op); } } __visible_for_testing __mockable void call_five_dsms_sign_err(const char *app, int result) { five_dsms_sign_err(app, result); } void five_audit_sign_err(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result) { char comm[TASK_COMM_LEN]; struct task_struct *tsk = task ? task : current; get_task_comm(comm, tsk); call_five_dsms_sign_err(comm, result); } __visible_for_testing __mockable void five_audit_msg(struct task_struct *task, struct file *file, const char *op, enum task_integrity_value prev, enum task_integrity_value tint, const char *cause, int result) { struct audit_buffer *ab; struct inode *inode = NULL; const char *fname = NULL; char *pathbuf = NULL; char filename[NAME_MAX]; char comm[TASK_COMM_LEN]; const char *name = ""; struct task_struct *tsk = task ? task : current; struct integrity_iint_cache *iint = NULL; if (file) { inode = file_inode(file); fname = five_d_path(&file->f_path, &pathbuf, filename); } ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_INTEGRITY_DATA); if (unlikely(!ab)) { pr_err("Can't get a context of audit logs\n"); goto exit; } audit_log_format(ab, " pid=%d", task_pid_nr(tsk)); audit_log_format(ab, " tgid=%d", task_tgid_nr(tsk)); audit_log_task_context(ab); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) audit_log_format(ab, " op=%s", op); #else audit_log_format(ab, " op="); audit_log_string(ab, op); #endif audit_log_format(ab, " cint=0x%x", tint); audit_log_format(ab, " pint=0x%x", prev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) audit_log_format(ab, " cause=%s", cause); #else audit_log_format(ab, " cause="); audit_log_string(ab, cause); #endif audit_log_format(ab, " comm="); audit_log_untrustedstring(ab, get_task_comm(comm, tsk)); if (fname) { audit_log_format(ab, " name="); audit_log_untrustedstring(ab, fname); name = fname; } if (inode) { audit_log_format(ab, " dev="); audit_log_untrustedstring(ab, inode->i_sb->s_id); audit_log_format(ab, " ino=%lu", inode->i_ino); audit_log_format(ab, " i_version=%llu ", inode_query_iversion(inode)); iint = integrity_inode_get(inode); if (iint) { audit_log_format(ab, " five_status=%d ", five_get_cache_status(iint)); audit_log_format(ab, " version=%llu ", (unsigned long long)iint->version); } } audit_log_format(ab, " res=%d", result); audit_log_end(ab); exit: if (pathbuf) __putname(pathbuf); } void five_audit_tee_msg(const char *func, const char *cause, int rc, uint32_t origin) { struct audit_buffer *ab; ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_INTEGRITY_DATA); if (unlikely(!ab)) { pr_err("Can't get a context of audit logs\n"); return; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) audit_log_format(ab, " func=%s", func); audit_log_format(ab, " cause=%s", cause); #else audit_log_format(ab, " func="); audit_log_string(ab, func); audit_log_format(ab, " cause="); audit_log_string(ab, cause); #endif audit_log_format(ab, " rc=0x%x, ", rc); audit_log_format(ab, " origin=0x%x", origin); audit_log_end(ab); } void five_audit_hexinfo(struct file *file, const char *msg, char *data, size_t data_length) { struct audit_buffer *ab; struct inode *inode = NULL; const unsigned char *fname = NULL; char filename[NAME_MAX]; char *pathbuf = NULL; struct integrity_iint_cache *iint = NULL; if (file) { fname = five_d_path(&file->f_path, &pathbuf, filename); inode = file_inode(file); } ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_INTEGRITY_DATA); if (unlikely(!ab)) { pr_err("Can't get a context of audit logs\n"); goto exit; } if (fname) { audit_log_format(ab, " name="); audit_log_untrustedstring(ab, fname); } if (inode) { audit_log_format(ab, " i_version=%llu ", inode_query_iversion(inode)); iint = integrity_inode_get(inode); if (iint) { audit_log_format(ab, " cache_value=%lu ", iint->five_flags); audit_log_format(ab, " five_status=%d ", five_get_cache_status(iint)); audit_log_format(ab, " version=%llu ", (unsigned long long)iint->version); } } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) audit_log_format(ab, "%s", msg); #else audit_log_string(ab, msg); #endif audit_log_n_hex(ab, data, data_length); audit_log_end(ab); exit: if (pathbuf) __putname(pathbuf); }