linux开启user_cmd功能记录用户所有操作指令到log message

本文介绍如何配置bash、csh及ksh环境变量以记录用户的每一步操作命令,并将这些信息发送到系统日志中,方便进行系统管理和审计。

修改如下/etc/下的如下文件,或者用户目录下的对应/root/.bash_profile等文件


bash.bashrc:export PROMPT_COMMAND='msg=$(history 1 | { read x y; echo "$y"; });user=$(whoami);loginuser=$(who am i); logger -t user_cmd "${msg} by ${user} from [$loginuser]"'
csh.cshrc.local:alias precmd 'set msg = `history 1|cut -f3-|sed -e "s|{||g" | sed -e "s|}||g"` ; set user = `whoami` ; set loginuser = `who am i`; logger -t user_cmd "${msg} by ${user} from [$loginuser]" >/dev/null'
ksh.kshrc:export PROMPT_COMMAND='msg=$(history 1 | { read x y; echo "$y"; });user=$(whoami);loginuser=$(who am i); logger -t user_cmd "${msg} by ${user} from [$loginuser]"'

 

生效后,效果如下:

ep  2 11:16:08 T3200-51 user_cmd: 2014-09-02 11:15:51 vi /etc/sudoers by root from [root     pts/2        2014-08-29 09:06 (10.135.29.97)]
Sep  2 11:16:11 T3200-51 user_cmd: 2014-09-02 11:16:11 cd /etc by root from [root     pts/2        2014-08-29 09:06 (10.135.29.97)]
Sep  2 11:16:19 T3200-51 user_cmd: 2014-09-02 11:16:16 grep "user_cmd" * -r by root from [root     pts/2        2014-08-29 09:06 (10.135.29.97)]

/* line discipline 示例 * * 目标/场景: * - 你的 4G LTE 模组通过 USB 枚举为 ttyACM(x),通常默认使用 N_TTY 行规程(用户态 read/poll 循环) * - 本示例提供一个可加载的内核模块,实现自定义行规程,把收发与缓冲放到内核中: * - 用户态对 /dev/ttyACM0 的 read/write 将走本行规程的 .read/.write * - 驱动收到的下行数据通过 .receive_buf(或 .receive_buf2)进入内核环形队列,用户 read 阻塞等待,避免忙轮询 * - 上行数据通过 .write 根据底层 write_room 进行发送,不足时阻塞等待 write_wakeup * * 仅包含行规程实现(不创建额外字符设备),按需由用户加载并附着到 tty。 * * 重要说明: * - 该实现偏“直通+缓冲”,不做协议解析(比如 AT/PPP 等),你可以在 .receive_buf/.write 中自由插入解析或打包逻辑 * - 行规程编号需要选择一个空闲的 ID,默认使用 29(很多发行版常为空闲),可通过模块参数 ldisc_num=xx 修改 * - 当前系统已注册的行规程可通过:cat /proc/tty/ldiscs 查看 * - 附着到某个 ttyACM 的方法(示例): * 1) insmod n_lteacm_ldisc.ko ldisc_num=29 * 2) 在用户态用 ioctl(TIOCSETD) 设置行规程号码为 29 * 样例 C 代码: * int ldisc = 29; * int fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY); * ioctl(fd, TIOCSETD, &ldisc); * 3) 之后对该 fd 的 read/write 将走本行规程;poll/epoll 也可用 * - 卸载模块前,应先确保所有使用该行规程的 tty 已切回其它行规程(例如 N_TTY),否则 tty_unregister_ldisc 会返回 -EBUSY * * 兼容性: * - 主要针对 Linux 4.14+ ~ 6.x * - 不同内核版本 .receive_buf 与 .receive_buf2 原型略有差异,下面做了简单版本判断 * * 调试: * - dmesg 中会打印关键日志 * - 可以通过模块参数开启更详细日志:debug=1 */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_ldisc.h> #include <linux/kfifo.h> #include <linux/wait.h> #include <linux/uaccess.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/version.h> #include <linux/sockios.h> #include "asm/string.h" #include "linux/bpf.h" #include "lte_driver.h" #define CLOUD_STATUS_CLIENT_INFO_PATH "/cloud_status/client_info" #define DMS_MSG_RESERVED 0x20 /* [1, 32]为系统保留消息,不允许用户使用; */ #define MSGID_GLOBAL_BASE (DMS_MSG_RESERVED + 1) #define DMS_MSG_LOCAL_BASE 0x5001 #define CAP_MID_BASE 0x7000 /* 为避免冲突,CAP子系统消息编码从0x7000开始。 */ #define NSD_MID_BASE 0x5001 #define CLOUD_SDK_MSG_ID (NSD_MID_BASE + 9) #define DEV_SERVICE_TOKEN_UPDATE_MSG_ID (NSD_MID_BASE + 29) #define VALIDATE_TOKEN_UPDATE_MSG_ID (NSD_MID_BASE + 28) #define BC_RING_WIFI_PUSH_MSG_ID (CAP_MID_BASE + 118) /* wifi事件推送消息 */ #define LOW_POWER_CLOUD_MSG_IN_MID (MSGID_GLOBAL_BASE + 41) /* 基础云相关消息 */ #define LTE_DRIVER_MSG_ID (MSGID_GLOBAL_BASE + 128) /* lte driver msg */ #define DMS_MSG_IS_GLOBAL(mid) (((mid) > DMS_MSG_RESERVED) && ((mid) < DMS_MSG_LOCAL_BASE)) #define SHARD_MSG_LEN (8 * 1024) #define OUT 1 #define IN 0 /* 模块参数:行规程编号、接收缓冲大小、是否打印调试日志 */ static int ldisc_num = 29; /* 默认 29,可通过 insmod n_lteacm_ldisc.ko ldisc_num=xx 指定 */ module_param(ldisc_num, int, 0444); MODULE_PARM_DESC(ldisc_num, "Line discipline number to register (check /proc/tty/ldiscs)"); static int rx_buf_kb = 4; /* 接收缓冲区大小(KB) */ module_param(rx_buf_kb, int, 0644); MODULE_PARM_DESC(rx_buf_kb, "RX ring buffer size in KB (default 64KB)"); static int event_rx_buf_cnt = 4; /* event 事件处理队列 */ module_param(event_rx_buf_cnt, int, 0644); MODULE_PARM_DESC(event_rx_buf_cnt, "IO_BUF_T.cmd_type == BC_CMD_TYPE_EVENT from lte buffer."); static int debug = 1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Enable debug logs (0=off, 1=on)"); /* 行规程的私有状态 */ struct lte_ldisc { struct tty_struct *tty; /* 数据接收缓存,收到完整的IO_BUF_T时拷贝数据到 rx_fifo 或 event_rx_fifio */ IO_BUF_T *io_buf; int io_buf_recved; /* 已经缓存到io_buf中的字节数 */ spinlock_t io_buf_lock; /* IO_BUF_T.cmd_type != BC_CMD_TYPE_EVENT 转发到用户空间 */ struct kfifo rx_fifo; /* IO_BUF_T.cmd_type == BC_CMD_TYPE_EVENT 数据缓存 直接由内核转发 */ struct kfifo event_rx_fifo; spinlock_t rx_lock; /* 保护 rx_fifo 的自旋锁 */ spinlock_t event_rx_lock; /* 保护 event_rx_fifo 的自旋锁 */ wait_queue_head_t read_wq; /* 用户空间 read 等待队列 */ /* 发送等待(当底层无空间时等待 write_wakeup) */ wait_queue_head_t write_wq; struct work_struct irq_work; /* 内核对模组事件处理的中断下半部 */ bool rx_overflow_once; /* 仅打印一次溢出提示,避免刷屏 */ /* 状态 */ bool opened; int cloud_msg_local_len; unsigned char *cloud_shard_message; }; /* * \brief __io_buf_avail, 构建一个完整IO_BUF_T还需要的字节数 */ static unsigned int __io_buf_avail(struct lte_ldisc *ld) { unsigned int result_len = 0; if(ld->io_buf_recved < MAX_IOCTL_CMD) { return sizeof(IO_BUF_T); } result_len = MAX_IOCTL_CMD + ld->io_buf->cmd_dlen - ld->io_buf_recved; if(ld->io_buf_recved > ld->io_buf->cmd_dlen + MAX_IOCTL_CMD) { LTE_ERR("[ERROR] dlen:<%d> recv<%d>", ld->io_buf->cmd_dlen, ld->io_buf_recved); return 0; } return MAX_IOCTL_CMD + ld->io_buf->cmd_dlen - ld->io_buf_recved; } static int __io_buf_size(struct lte_ldisc *ld) { return ld->io_buf_recved; } /* * \param dir, 1: out, 0: in * * */ static void print_hex(const unsigned char *data, int len, int dir) { int i = 0; if(NULL == data || 0 == debug) { return; } if(dir) { LTE_INFO("<<["); } else { LTE_INFO(">>["); } for(i = 0; i < len; ++i) { if((i % 10) == 0) { printk(KERN_INFO "\n"); } if (data[i] >= 0x20 && data[i] <= 0x7E) { // 可打印字符,直接显示字符 printk(KERN_CONT "%c ", data[i]); } else { // 不可打印字符,显示十六进制 printk(KERN_CONT "%02x ", data[i]); } } LTE_INFO("]"); } /* 工具函数:安全获取/设置 tty->disc_data */ static inline struct lte_ldisc *ldisc_get(struct tty_struct *tty) { return tty ? (struct lte_ldisc *)tty->disc_data : NULL; } static inline void ldisc_set(struct tty_struct *tty, struct lte_ldisc *ld) { if (tty) tty->disc_data = ld; } static void lte_ldisc_close(struct tty_struct *tty) { struct lte_ldisc *ld = ldisc_get(tty); if (!ld) return; ld->opened = false; /* 唤醒可能阻塞的读写 */ wake_up_interruptible_all(&ld->read_wq); wake_up_interruptible_all(&ld->write_wq); /* 释放 FIFO */ kfifo_free(&ld->rx_fifo); kfifo_free(&ld->event_rx_fifo); /* 释放 接收缓存 */ kfree(ld->io_buf); kfree(ld->cloud_shard_message); ldisc_set(tty, NULL); kfree(ld); LTE_INFO("closed on %s\n", tty ? tty_name(tty) : "(null)"); } static int lte_ldisc_hangup(struct tty_struct *tty) { /* 按惯例等价于 close,TTY 层会确保序列 */ lte_ldisc_close(tty); LTE_INFO("hangup on %s\n", tty ? tty_name(tty) : "(null)"); return 0; } /* 注意:底层驱动/软中断调用,务必使用不可睡眠的同步原语(自旋锁等) */ static void lte_driver_recv_irq(struct tty_struct *tty, const unsigned char *data, char *fp, int count) { struct lte_ldisc *ld = NULL; unsigned int need_notify = 0; /* 是否通知应用程序 */ unsigned int need_schedule = 0; /* 是否调度中断下半部 */ unsigned int avail = 0; /* IO_BUF所需空间 */ unsigned int kavail = 0; /* FIFO剩余空间 */ unsigned long io_buf_irqf = 0; unsigned int io_buf_remain = 0; if(NULL == tty) { LTE_INFO("tty is null."); return; } ld = ldisc_get(tty); LTE_INFO("<<========================="); if (!ld || !ld->opened || count <= 0 || !data) { LTE_ERR("param check fail."); return; } spin_lock_irqsave(&ld->io_buf_lock, io_buf_irqf); avail = __io_buf_avail(ld); if ((unsigned int)count <= avail) /* 未构成一个完整数据包 */ { LTE_INFO("cp to io_buf: (c:<%d> + r:<%d>)/<%u>", count, ld->io_buf_recved,avail); memcpy(ld->io_buf + ld->io_buf_recved, data, count); ld->io_buf_recved += count; } else { if (avail) /* 构成完整的包 */ { LTE_WARN("Some Data May Not Trans By IO_BUF_T Nerver Run Here."); memcpy(ld->io_buf->data + ld->io_buf_recved, data, avail); ld->io_buf_recved += avail; io_buf_remain = count - avail; } } avail = __io_buf_avail(ld); if(0 != avail) /* 未获取到完整的数据包 */ { LTE_INFO("irq once io_buf: recv:<%d>/allNeed:<%u> avail:<%u>", ld->io_buf_recved, MAX_IOCTL_CMD + ld->io_buf->cmd_dlen, avail); spin_unlock_irqrestore(&ld->io_buf_lock, io_buf_irqf); return; } /* 分析数据包,根据cmd_type字段放入不同fifo */ if(BC_CMD_TYPE_EVENT == ld->io_buf->cmd_type) { /* 只有EVENT字段才需要内核处理 */ LTE_INFO("recv BC_CMD_TYPE_EVENT msg dlen:<%d>!!!!!!!!!!", __io_buf_size(ld) - MAX_IOCTL_CMD); print_hex(ld->io_buf->cmd_data, ld->io_buf->cmd_dlen, OUT); spin_lock(&ld->event_rx_lock); kavail = kfifo_avail(&ld->event_rx_fifo); if(kavail >= __io_buf_size(ld)) { LTE_INFO("Msg push to event_rx_fifo"); need_schedule = kfifo_in(&ld->event_rx_fifo, ld->io_buf->data, ld->io_buf_recved); } else { LTE_WARN("event_rx_fifo size need bigger, data overflow, this package may drop."); } spin_unlock(&ld->event_rx_lock); } else { LTE_INFO("recv msg<%d> len:<%d>", ld->io_buf->cmd_type, __io_buf_size(ld)); print_hex(ld->io_buf->cmd_data, ld->io_buf->cmd_dlen, OUT); /* 通知应用程序收包 */ spin_lock(&ld->rx_lock); kavail = kfifo_avail(&ld->rx_fifo); if(kavail >= __io_buf_size(ld)) { LTE_INFO("Msg push to rx_fifo"); need_notify = kfifo_in(&ld->rx_fifo, ld->io_buf->data, ld->io_buf_recved); } else { LTE_WARN("rx_fifo size need bigger, data overflow, this package may drop."); } /* 更新 receive_room,给驱动一个“还剩多少余量”的提示 */ tty->receive_room = kfifo_avail(&ld->rx_fifo); spin_unlock(&ld->rx_lock); } memset(ld->io_buf->data, 0, sizeof(IO_BUF_T)); ld->io_buf_recved = 0; if(io_buf_remain > 0) { memcpy(ld->io_buf->data, data + count - io_buf_remain, io_buf_remain); ld->io_buf_recved = io_buf_remain; } spin_unlock_irqrestore(&ld->io_buf_lock, io_buf_irqf); /* 通知等待进程 */ if (need_notify) { wake_up_interruptible(&ld->read_wq); } /* 调度工作队列 */ if (need_schedule) { schedule_work(&ld->irq_work); } } /* ========== 行规程回调:写可用通知 write_wakeup(底层有空间时调用) ========== */ static void lte_ldisc_write_wakeup(struct tty_struct *tty) { struct lte_ldisc *ld = ldisc_get(tty); if (!ld || !ld->opened) return; wake_up_interruptible(&ld->write_wq); } extern uint32_t msg_send(uint32_t mid, uint8_t *mbuf, uint32_t mlen); /* * \brief lte_msg_send, if(mid > DMS_MSG_RESERVED && mid < DMS_MSG_LOCAL_BASE) * Send directly. * else * Send LTE_DRIVER_MSG_ID msg. */ static uint32_t lte_msg_send(uint32_t mid, uint8_t *mbuf, uint32_t mlen) { LTE_DRIVER_MSG msg; if(DMS_MSG_IS_GLOBAL(mid)) { LTE_INFO("Send global msg:<%u>, len:<%u>", mid, mlen); return msg_send(mid, mbuf, mlen); } msg.mid = mid; msg.mbuf = mbuf; msg.mlen = mlen; LTE_INFO("Send LTE_DRIVER_MSG:<%u>, len:<%u>", mid, mlen); return msg_send(LTE_DRIVER_MSG_ID, (uint8_t*)&msg, sizeof(LTE_DRIVER_MSG)); } static int send_to_mqtt_proxy(uint8_t *payload, int payload_len) { if (payload == NULL){ return -1; } return lte_msg_send(LOW_POWER_CLOUD_MSG_IN_MID, (uint8_t *)payload, payload_len); } static void broadcast_cloud_connect_status(int conn_status, int last_conn_status) { CLOUD_SDK_MSG conn_status_msg = {0}; memset(&conn_status_msg, 0, sizeof(conn_status_msg)); conn_status_msg.conn_status = conn_status > 0 ? 1 : 0; conn_status_msg.last_conn_status = last_conn_status; lte_msg_send(CLOUD_SDK_MSG_ID, (u8 *)&conn_status_msg, sizeof(conn_status_msg)); return; } static void lte_ioctl_cb(struct lte_ldisc *ld, IO_BUF_T *cmd_buf) { static unsigned char piece = 0; static unsigned char shard_id = 0; if(NULL == ld || NULL == cmd_buf) { return; } switch (cmd_buf->cmd_type) { case BC_CMD_TYPE_PUBLISH: case BC_CMD_TYPE_GET: case BC_CMD_TYPE_SET: case BC_CMD_TYPE_ACK: LTE_WARN("Message:<%d> distribution error.", cmd_buf->cmd_type); break; case BC_CMD_TYPE_EVENT: LTE_INFO("recv event name:%d, dlen:%d\n", cmd_buf->cmd_name, cmd_buf->cmd_dlen); print_hex(cmd_buf->cmd_data, cmd_buf->cmd_dlen, OUT); switch(cmd_buf->cmd_name) { case BC_EVENT_CLOUD_CONNECT: LTE_INFO(">>>>>>>>>>>>>>>>>> connect cloud connect <<<<<<<<<<<<<<<<<<\n"); broadcast_cloud_connect_status(1, -1); break; case BC_EVENT_CLOUD_DISCONNECT: broadcast_cloud_connect_status(0, -1); break; case BC_EVENT_VALIDATE_TOKEN_UPDATE: lte_msg_send(VALIDATE_TOKEN_UPDATE_MSG_ID, NULL, 0); break; case BC_EVENT_DEV_TOKEN_UPDATE: lte_msg_send(DEV_SERVICE_TOKEN_UPDATE_MSG_ID, NULL, 0); break; case BC_EVENT_IOT_CHIP_LOG: LTE_INFO("SHIP2LOG %s\n", cmd_buf->cmd_data + 1); break; case BC_EVENT_RING_MSG: lte_msg_send(BC_RING_WIFI_PUSH_MSG_ID, cmd_buf->cmd_data + 1, cmd_buf->cmd_dlen - 1); break; #ifdef BATTERY_STATISTIC_SUPPORT case BC_EVENT_BAT_DATA_PREPARE: LTE_INFO("date ok,get bat_stat_data"); send_bat_stat_data(); break; #endif case BC_EVENT_CLOUD_MESSAGE: // cmd_data首字节是事件计数,实际长度是cmd_dlen - 1 LTE_ERR("CLOUD MSG EVENT, len=%d\n", cmd_buf->cmd_dlen - 1); if (cmd_buf->cmd_dlen - 1 < (sizeof(cmd_buf->cmd_data) - 1)) { cmd_buf->cmd_data[cmd_buf->cmd_dlen] = 0; send_to_mqtt_proxy(&cmd_buf->cmd_data[1], cmd_buf->cmd_dlen); } else { LTE_ERR("buf->cmd_dlen too long:%d", cmd_buf->cmd_dlen); send_to_mqtt_proxy(&cmd_buf->cmd_data[1], cmd_buf->cmd_dlen - 1); } break; // TODO: 补充其他事件处理 case BC_EVENT_CLOUD_MESSAGE_PORTION: if (piece == 0) { shard_id = cmd_buf->cmd_data[2]; } if ((piece == cmd_buf->cmd_data[1]) && (ld->cloud_msg_local_len + cmd_buf->cmd_dlen - 3 <= SHARD_MSG_LEN) && (shard_id == cmd_buf->cmd_data[2])) { memcpy(ld->cloud_shard_message + ld->cloud_msg_local_len, &cmd_buf->cmd_data[3], cmd_buf->cmd_dlen - 3); ld->cloud_msg_local_len += cmd_buf->cmd_dlen - 3; piece++; } else { piece = 0; ld->cloud_msg_local_len = 0; memset(ld->cloud_shard_message, 0, SHARD_MSG_LEN); } break; case BC_EVENT_CLOUD_MESSAGE_FINAL: if ((piece == cmd_buf->cmd_data[1]) && (ld->cloud_msg_local_len + cmd_buf->cmd_dlen - 2 <= SHARD_MSG_LEN) && (shard_id == cmd_buf->cmd_data[2])) { memcpy(ld->cloud_shard_message + ld->cloud_msg_local_len, &cmd_buf->cmd_data[3], cmd_buf->cmd_dlen - 3); ld->cloud_msg_local_len += cmd_buf->cmd_dlen - 3; send_to_mqtt_proxy(ld->cloud_shard_message, ld->cloud_msg_local_len); } piece = 0; ld->cloud_msg_local_len = 0; memset(ld->cloud_shard_message, 0, SHARD_MSG_LEN); break; case BC_EVENT_WIFI_WATCHDOG: LTE_INFO("[CB]WIFI WATCHDOG EVENT :%d\n", cmd_buf->cmd_data[1]); //add wdt? break; default: break; } break; default: LTE_ERR("unknown type:%d cmd:%d\n", cmd_buf->cmd_type, cmd_buf->cmd_name); break; } } static void irq_work_handler(struct work_struct *work) { struct lte_ldisc *ld = container_of(work, struct lte_ldisc, irq_work); unsigned long event_rx_buf_irqf = 0; int fifo_len = 0, read_len; IO_BUF_T io_buf; memset(&io_buf, 0, sizeof(IO_BUF_T)); LTE_INFO("handler irq"); spin_lock_irqsave(&ld->event_rx_lock, event_rx_buf_irqf); fifo_len = kfifo_len(&ld->event_rx_fifo); if(0 == fifo_len || fifo_len < MAX_IOCTL_CMD) { LTE_WARN("event_rx_fifo empty but wake up handler."); return; } read_len = kfifo_out(&ld->event_rx_fifo, &io_buf.data, MAX_IOCTL_CMD); if(io_buf.cmd_dlen > kfifo_len(&ld->event_rx_fifo)) { LTE_WARN("event_rx_fifo data unparse fail."); kfifo_reset(&ld->event_rx_fifo); return; } kfifo_out(&ld->event_rx_fifo, &io_buf.cmd_data, io_buf.cmd_dlen); spin_unlock_irqrestore(&ld->event_rx_lock, event_rx_buf_irqf); /* 开始协议解析部分 */ lte_ioctl_cb(ld, &io_buf); } static ssize_t lte_ioctl_read(struct tty_struct *tty, unsigned char __user *buf) { struct lte_ldisc *ld = ldisc_get(tty); unsigned int copied_total = 0; IO_BUF_T cmd_header; unsigned int fifo_len = 0; unsigned long irqf = 0; int ret = 0; if (!ld || !ld->opened) { LTE_ERR("param check fail."); return -EIO; } LTE_INFO("read data from ioctl cb."); spin_lock_irqsave(&ld->rx_lock, irqf); fifo_len = kfifo_len(&ld->rx_fifo); if(fifo_len < MAX_IOCTL_CMD) { spin_unlock_irqrestore(&ld->rx_lock, irqf); /* 阻塞等待 receive_buf 唤醒,期间可被信号中断 */ ret = wait_event_interruptible(ld->read_wq, ({ unsigned int l; unsigned long f; spin_lock_irqsave(&ld->rx_lock, f); l = kfifo_len(&ld->rx_fifo); spin_unlock_irqrestore(&ld->rx_lock, f); l >= MAX_IOCTL_CMD || !ld->opened; })); if (ret) return -1; if (!ld->opened) { return -EIO; } spin_lock_irqsave(&ld->rx_lock, irqf); } fifo_len = kfifo_len(&ld->rx_fifo); if(fifo_len < MAX_IOCTL_CMD) { spin_unlock_irqrestore(&ld->rx_lock, irqf); LTE_ERR("False wake-up"); return -1; } memset(&cmd_header, 0, sizeof(IO_BUF_T)); kfifo_out_peek(&ld->rx_fifo, cmd_header.data, MAX_IOCTL_CMD); if(fifo_len < cmd_header.cmd_dlen + MAX_IOCTL_CMD) { spin_unlock_irqrestore(&ld->rx_lock, irqf); LTE_ERR("False wake-up"); return -1; } kfifo_out(&ld->rx_fifo, cmd_header.data, MAX_IOCTL_CMD + cmd_header.cmd_dlen); copy_to_user(buf, cmd_header.data, MAX_IOCTL_CMD + cmd_header.cmd_dlen); tty->receive_room = kfifo_avail(&ld->rx_fifo); spin_unlock_irqrestore(&ld->rx_lock, irqf); LTE_INFO("read <%d> bytes success.", MAX_IOCTL_CMD + cmd_header.cmd_dlen); return copied_total; } /* ========== 行规程回调:用户态 read/write/poll ========== */ static ssize_t lte_ldisc_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr, void **cookie, unsigned long offset) { struct lte_ldisc *ld = ldisc_get(tty); ssize_t copied_total = 0; int ret = 0; LTE_ERR("Warning This method should not be Called."); if (!ld || !ld->opened) { LTE_ERR("param check fail."); return -EIO; } if (nr == 0) { return 0; } /* 循环直到满足用户请求或遇到阻塞条件 */ while (nr) { size_t to_copy; unsigned int fifo_len; unsigned long irqf; unsigned char *tmp; /* 尝试从 FIFO 拿数据,如果没有则根据 O_NONBLOCK 决定是否睡眠等待 */ spin_lock_irqsave(&ld->rx_lock, irqf); fifo_len = kfifo_len(&ld->rx_fifo); spin_unlock_irqrestore(&ld->rx_lock, irqf); if (fifo_len == 0) { /* 若已有部分数据,先返回(避免读到部分就阻塞) */ if (copied_total > 0) break; if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; } /* 阻塞等待 receive_buf 唤醒,期间可被信号中断 */ ret = wait_event_interruptible(ld->read_wq, ({ unsigned int l; unsigned long f; spin_lock_irqsave(&ld->rx_lock, f); l = kfifo_len(&ld->rx_fifo); spin_unlock_irqrestore(&ld->rx_lock, f); l > 0 || !ld->opened; })); if (ret) break; /* -ERESTARTSYS 等,用户态可重试 */ if (!ld->opened) { ret = -EIO; break; } continue; } /* 本次最多读取 nr 与 fifo_len 中较小的数量 */ to_copy = min_t(size_t, nr, fifo_len); /* 为了避免在持有自旋锁时 copy_to_user(可能睡眠),先把数据出队到临时缓冲 */ tmp = kmalloc(to_copy, GFP_KERNEL); if (!tmp) { ret = -ENOMEM; break; } spin_lock_irqsave(&ld->rx_lock, irqf); to_copy = kfifo_out(&ld->rx_fifo, tmp, to_copy); tty->receive_room = kfifo_avail(&ld->rx_fifo); spin_unlock_irqrestore(&ld->rx_lock, irqf); /* 复制到用户空间 */ memcpy(buf, tmp, to_copy); print_hex(tmp, to_copy, OUT); kfree(tmp); buf += to_copy; nr -= to_copy; copied_total += to_copy; /* 一次读够了,返回 */ if (nr == 0) break; /* FIFO 可能还有数据,继续下一轮;也可能为空,需要等待 */ } /* 若已读出部分数据,优先返回字节数;否则返回错误码 */ return (copied_total > 0) ? copied_total : ret; } static ssize_t lte_ldisc_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) { struct lte_ldisc *ld = NULL; ssize_t written_total = 0; unsigned char *kbuf = NULL; int ret = 0; if(NULL == tty) { LTE_ERR("tty is null."); return -ENOENT; } ld = ldisc_get(tty); if(NULL == ld) { LTE_ERR("tty is not set lte_ldisc private."); return -1; } if(buf == NULL) { LTE_ERR("user buf is null."); return -1; } LTE_INFO("Call lte_ldisc_write with: nr<%u>", nr); if (!ld || !ld->opened) { LTE_ERR("param check fail."); return -EIO; } if (nr == 0) return 0; kbuf = kmalloc(nr, GFP_KERNEL); if (!kbuf) { LTE_ERR("kbuf malloc error request:<%d> bytes.", nr); return -ENOMEM; } memcpy(kbuf, buf, nr); LTE_INFO("Write Bytes:<%d>", nr); print_hex(kbuf, nr, IN); while (written_total < nr) { int room = 0; int to_send = 0; int sent = 0; /* 查询底层可写空间 */ room = tty_write_room(tty); LTE_INFO("tty_write_room: %d.", room); if (room <= 0) { LTE_WARN("Write Buffer is empty!!!"); /* 若已写出部分,先返回;否则根据是否非阻塞确定是否等待 */ if (written_total > 0) { break; } if (file->f_flags & O_NONBLOCK) { LTE_INFO("O_NONBLOCK EAGAIN."); ret = -EAGAIN; break; } /* 阻塞等待底层 write_wakeup */ ret = wait_event_interruptible(ld->write_wq, ({ int r = tty_write_room(tty); r > 0 || !ld->opened; })); if (ret) break; if (!ld->opened) { LTE_ERR("Write but tty is closed."); ret = -EIO; break; } continue; } /* 按底层空间发送 */ to_send = min_t(int, room, (int)(nr - written_total)); sent = tty->ops->write(tty, kbuf + written_total, to_send); if (sent < 0) { LTE_ERR("tty write fail with:<%d>", ret); ret = sent; break; } else if (sent == 0) { LTE_WARN("tty write return 0"); /* 某些驱动可能短暂返回 0,避免忙等稍作让步或等待 */ if (file->f_flags & O_NONBLOCK) { ret = (written_total > 0) ? written_total : -EAGAIN; break; } /* 轻微让步,或等待 write_wakeup */ schedule_timeout_interruptible(msecs_to_jiffies(5)); continue; } written_total += sent; } kfree(kbuf); LTE_INFO("write finished, write total:<%d> ret:<%d>", written_total, ret); return (written_total > 0) ? written_total : ret; } static __poll_t lte_ldisc_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { struct lte_ldisc *ld = ldisc_get(tty); __poll_t mask = 0; unsigned long irqf; unsigned int fifo_len; if (!ld || !ld->opened) return EPOLLERR | EPOLLHUP; /* 将等待队列加入到轮询表中,以支持 select/poll/epoll */ poll_wait(file, &ld->read_wq, wait); poll_wait(file, &ld->write_wq, wait); /* 可读? */ spin_lock_irqsave(&ld->rx_lock, irqf); fifo_len = kfifo_len(&ld->rx_fifo); spin_unlock_irqrestore(&ld->rx_lock, irqf); if (fifo_len > 0) mask |= EPOLLIN | EPOLLRDNORM; /* 可写? */ if (tty_write_room(tty) > 0) { mask |= EPOLLOUT | EPOLLWRNORM; } return mask; } static int lte_ldisc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { int ret = -1; IO_BUF_T tmp_buffer; /* 透传未知命令给用户态(返回 -ENOIOCTLCMD 让 TTY 栈尝试其它处理) */ LTE_INFO("lte_ldisc_ioctl called cmd:<%u>", cmd); switch(cmd) { case SIOCDEVPRIVATE: memset(&tmp_buffer, 0, sizeof(IO_BUF_T)); if(copy_from_user(&tmp_buffer.data, (void*)arg, MAX_IOCTL_CMD)) { LTE_ERR("ioctl user buffer can't access."); break; } if(copy_from_user(&tmp_buffer.cmd_data, (char*)arg + MAX_IOCTL_CMD, tmp_buffer.cmd_dlen)) { LTE_ERR("ioctl user buffer can't access."); break; } LTE_INFO("receive ioctl cmd_type:<%d> cmd_name:<%d> cmd_length:<%u> cmd_data:", tmp_buffer.cmd_type, tmp_buffer.cmd_name, tmp_buffer.cmd_dlen); ret = lte_ldisc_write(tty, file, (unsigned char*)&tmp_buffer.data, tmp_buffer.cmd_dlen + MAX_IOCTL_CMD); break; default: LTE_INFO("lte_ldisc not support cmd:<%u>.", cmd); ret = -ENOIOCTLCMD; break; } ret = lte_ioctl_read(tty, (void*)arg); LTE_INFO("lte_ioctl_read ret:<%d>", ret); return ret >= 0 ? 0 : -1; } static int lte_ldisc_open(struct tty_struct *tty) { struct lte_ldisc *ld = NULL; int ret = -EINVAL; LTE_INFO("open tty <%s>", tty_name(tty)); if (!tty) { LTE_ERR("tty is null."); return -EINVAL; } ld = kzalloc(sizeof(*ld), GFP_KERNEL); if (!ld) { LTE_ERR("kzalloc empty."); return -ENOMEM; } /* 分配接收缓存 */ ld->io_buf = kzalloc(sizeof(IO_BUF_T), GFP_KERNEL); if(NULL == ld->io_buf) { LTE_ERR("kzalloc io_buf empty."); goto err_rx_fifo; } ld->tty = tty; spin_lock_init(&ld->rx_lock); spin_lock_init(&ld->event_rx_lock); spin_lock_init(&ld->io_buf_lock); init_waitqueue_head(&ld->read_wq); init_waitqueue_head(&ld->write_wq); INIT_WORK(&ld->irq_work, irq_work_handler); /* 分配接收环形缓冲区 */ ret = kfifo_alloc(&ld->rx_fifo, rx_buf_kb * 1024, GFP_KERNEL); if (ret) { LTE_ERR("kfifo_alloc(%dKB) failed: %d\n", rx_buf_kb, ret); goto err_rx_fifo; } ret = kfifo_alloc(&ld->event_rx_fifo, event_rx_buf_cnt * sizeof(IO_BUF_T), GFP_KERNEL); if (ret) { LTE_ERR("kfifo_alloc(%dKB) failed: %d\n", event_rx_buf_cnt * sizeof(IO_BUF_T), ret); goto err_event_tx_fifo; } ld->cloud_shard_message = kzalloc(SHARD_MSG_LEN, GFP_KERNEL); if(!ld->cloud_shard_message) { LTE_ERR("alloc cloud_shard_message fail."); goto err_cloud_shard_message; } /* 告诉底层驱动我们目前能接收的空间 */ tty->receive_room = kfifo_avail(&ld->rx_fifo); ldisc_set(tty, ld); ld->opened = true; LTE_INFO("opened on %s (RX %dKB), (TX %dKB), ldisc=%d\n", tty_name(tty), rx_buf_kb, event_rx_buf_cnt * sizeof(IO_BUF_T), ldisc_num); return 0; err_cloud_shard_message: kfifo_free(&ld->event_rx_fifo); err_event_tx_fifo: kfifo_free(&ld->rx_fifo); err_rx_fifo: kfree(ld); ld = NULL; return ret; } /* ========== 行规程 ops 描述符 ========== */ static struct tty_ldisc_ops lte_ldisc_ops = { .owner = THIS_MODULE, .name = "n_lteacm", .open = lte_ldisc_open, .close = lte_ldisc_close, .hangup = lte_ldisc_hangup, .read = lte_ldisc_read, .write = lte_ldisc_write, .poll = lte_ldisc_poll, .ioctl = lte_ldisc_ioctl, .receive_buf = lte_driver_recv_irq, .write_wakeup = lte_ldisc_write_wakeup, /* 暂时不处理这些接口 * .set_termios = NULL, * .flush_buffer = NULL, * .throttle = NULL, * .unthrottle = NULL, */ }; /* ========== 模块加载/卸载 ========== */ static int __init lte_ldisc_init(void) { int ret = -EINVAL; if (ldisc_num < 0 || ldisc_num >= NR_LDISCS) { LTE_ERR("invalid ldisc_num=%d, valid range [0, %d)\n", ldisc_num, NR_LDISCS); return -EINVAL; } ret = tty_register_ldisc(ldisc_num, &lte_ldisc_ops); if (ret) { LTE_ERR("tty_register_ldisc(%d) failed: %d\n", ldisc_num, ret); return ret; } LTE_INFO("registered ldisc=%d, RX=%dKB, debug=%d, version=%s\n", ldisc_num, rx_buf_kb, debug, DRV_VERSION); LTE_INFO("Attach with TIOCSETD(%d) on your /dev/ttyACM*\n", ldisc_num); return 0; } static void __exit lte_ldisc_exit(void) { int ret = 0; ret = tty_unregister_ldisc(ldisc_num); if (ret) { /* 若返回 -EBUSY,说明仍有 tty 使用该行规程,需先切回其它行规程再卸载模块 */ LTE_ERR("tty_unregister_ldisc(%d) failed: %d (busy?)\n", ldisc_num, ret); /* 通常此时 rmmod 会失败,因为模块被引用;用户需先 detach 然后重试卸载 */ } else { LTE_INFO("unregistered ldisc=%d\n", ldisc_num); } } module_init(lte_ldisc_init); module_exit(lte_ldisc_exit); MODULE_AUTHOR("liujiarui <liujiarui@tp-link.com.hk>"); MODULE_DESCRIPTION(DRV_DESC); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); 这段代码中可能到只Kernel panic的地方
最新发布
09-05
<think>我们被要求配置auditd来监控两个关键事件: 1. 对/etc/passwd文件的写入(w)和属性更改(a)操作 2. 对/usr/sbin/目录下的执行(x)操作 根据引用[1]和引用[2]中的信息,我们可以通过创建或修改auditd规则文件来实现。 具体步骤: 步骤1:创建或修改规则文件 规则文件通常放在/etc/audit/rules.d/目录下,例如audit.rules(也可以是其他以.rules结尾的文件)。引用[1]和引用[2]都提到了这个目录。 步骤2:添加规则 规则语法: - 监控文件:-w 文件路径 -p 权限 -k 关键字 - 监控系统调用:-a always,exit -F 参数 -S 系统调用 -k 关键字 对于第一个要求(/etc/passwd): - 写入操作(w)和属性更改(a)对应权限为wa,因此规则为: -w /etc/passwd -p wa -k passwd_changes 对于第二个要求(/usr/sbin/目录下的执行操作): - 由于是监控目录下的所有文件被执行,我们可以使用-w来监控整个目录,并设置权限为x(执行): -w /usr/sbin/ -p x -k privileged_exec - 注意:监控目录时,auditd会监控该目录下所有文件的执行操作,但不会递归到子目录(除非使用递归选项,但默认没有递归)。如果需要递归监控,可以使用规则监控execve系统调用并指定路径,但这样可能会产生大量日志。这里我们按题目要求监控该目录(不递归)即可。 步骤3:加载规则 添加规则后,需要让auditd重新加载规则才能生效。可以使用auditctl命令加载,或者重启auditd服务。但引用[3]提到,在启动过程中,/etc/audit/audit.rules中的规则会被加载。另外,引用[3]还提到了augenrules工具,它可以将/etc/audit/rules.d/中的规则编译成audit.rules文件。因此,我们可以使用augenrules来编译并加载规则,也可以使用service重启。 步骤4:验证规则 使用auditctl -l列出当前加载的规则,检查是否包含我们添加的规则。 详细操作: 1. 编辑规则文件(例如/etc/audit/rules.d/audit.rules): sudo vi /etc/audit/rules.d/audit.rules 2. 在文件中添加以下规则: # 监控/etc/passwd的写和属性更改 -w /etc/passwd -p wa -k passwd_changes # 监控/usr/sbin/目录下的执行操作 -w /usr/sbin/ -p x -k privileged_exec 3. 保存文件。 4. 加载规则: 方法1:使用augenrules(推荐,因为引用[3]提到这个工具会编译rules.d下的所有规则) sudo augenrules --load 方法2:或者重启auditd服务 sudo service auditd restart # 或者 sudo systemctl restart auditd 5. 验证规则: sudo auditctl -l 预期输出应该包含我们添加的两条规则。 6. 测试规则: - 测试/etc/passwd:尝试修改/etc/passwd(例如,使用vipw命令或者添加一个测试用户,然后删除)或者更改属性(如chmod)。然后使用ausearch -k passwd_changes查看日志。 - 测试/usr/sbin/下的执行:运行一个/usr/sbin/下的命令(如/usr/sbin/adduser),然后使用ausearch -k privileged_exec查看日志。 注意:生产环境谨慎测试,避免误操作。 引用[4]提到可以通过man手册查看auditd和工具的使用说明,如果有疑问可以查阅。 另外,关于规则文件的位置,不同的Linux发行版可能略有不同,但通常都是/etc/audit/rules.d/目录。如果系统没有该目录,可能是auditd版本较旧,可能需要直接编辑/etc/audit/audit.rules文件(引用[1]提到过这个文件)。 因此,如果系统没有/etc/audit/rules.d/目录,则可以直接编辑/etc/audit/audit.rules文件,并在文件末尾添加规则,然后重启auditd服务。 总结:我们通过两条规则实现了监控需求,并说明了如何加载和验证规则。 生成相关问题: 1. 如何监控一个目录及其子目录下的所有文件访问? 2. auditd规则中的关键字(-k)有什么作用? 3. 如何查看auditd产生的日志? 4. 如何设置auditd日志的轮转和保留策略? 5. 如何监控特定用户执行的操作?</think>### 配置auditd监控文件操作与特权命令的完整方案 #### 一、核心配置步骤 1. **创建永久审计规则文件** ```bash sudo vi /etc/audit/rules.d/file-monitoring.rules ``` 2. **添加监控规则**(参考[^1][^2]) ```conf # 监控/etc/passwd的写入(w)和属性更改(a) -w /etc/passwd -p wa -k passwd_changes # 监控/usr/sbin/目录下所有可执行文件的执行操作(x) -w /usr/sbin/ -p x -k privileged_exec # 监控所有命令执行(可选增强) -a always,exit -F arch=b64 -S execve -k command_exec -a always,exit -F arch=b32 -S execve -k command_exec ``` 3. **加载规则并重启服务** ```bash sudo augenrules --load # 编译并加载所有规则[^3] sudo service auditd restart ``` #### 二、关键配置解析 | 规则参数 | 作用说明 | 示例场景 | |---------|---------|---------| | `-w 路径` | 监控指定路径 | `/etc/passwd` | | `-p 权限` | 监控的操作类型:<br>• `r`=读取<br>• `w`=写入<br>• `x`=执行<br>• `a`=属性变更 | `wa`=写+属性变更 | | `-k 关键词` | 日志事件分类标签 | `passwd_changes` | | `-a always,exit` | 监控系统调用退出事件 | `execve`调用 | #### 三、验证配置有效性 1. **检查加载的规则** ```bash sudo auditctl -l ``` 预期输出: ``` -w /etc/passwd -p wa -k passwd_changes -w /usr/sbin -p x -k privileged_exec ``` 2. **测试监控功能** ```bash # 触发passwd变更测试 sudo touch /etc/passwd # 触发属性变更 sudo echo "test" >> /etc/passwd # 触发写入 # 触发特权命令测试 sudo /usr/sbin/useradd testuser ``` 3. **查询审计日志** ```bash # 查看passwd相关日志 sudo ausearch -k passwd_changes -i # 查看特权命令日志 sudo ausearch -k privileged_exec -i ``` 典型日志输出: ``` type=PROCTITLE msg=audit(2023-08-01 14:30:22) : proctitle=sudo touch /etc/passwd type=PATH msg=... item=0 name=/etc/passwd inode=504 dev=fd:00 mode=file,644 ``` #### 四、高级配置建议 1. **日志增强策略** ```conf # 记录用户ID和原始终端 -a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands ``` 2. **防止规则篡改** ```bash sudo chattr +i /etc/audit/rules.d/file-monitoring.rules ``` 3. **日志轮转配置** ```bash sudo vi /etc/audit/auditd.conf ``` 修改关键参数: ```conf num_logs = 30 # 保留30个日志文件 max_log_file = 50 # 单个日志最大50MB ``` #### 五、排错指南 1. **规则未加载** - 检查`augenrules --load`输出 - 验证`/etc/audit/audit.rules`是否包含自定义规则 2. **无日志生成** ```bash sudo auditctl -s # 检查enabled标志 sudo service auditd status # 检查服务状态 ``` 3. **日志查询失败** ```bash aureport --key # 列出所有关键词事件统计 ``` > **操作提示**:使用`man auditd`查看完整手册[^4],调试时启用实时监控`auditctl -m "TEST MESSAGE"`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值