1 /* 2 * Creating audit events from TTY input. 3 * 4 * Copyright (C) 2007 Red Hat, Inc. All rights reserved. This copyrighted 5 * material is made available to anyone wishing to use, modify, copy, or 6 * redistribute it subject to the terms and conditions of the GNU General 7 * Public License v.2. 8 * 9 * Authors: Miloslav Trmac <mitr@redhat.com> 10 */ 11 12 #include <linux/audit.h> 13 #include <linux/slab.h> 14 #include <linux/tty.h> 15 16 struct tty_audit_buf { 17 struct mutex mutex; /* Protects all data below */ 18 dev_t dev; /* The TTY which the data is from */ 19 unsigned icanon:1; 20 size_t valid; 21 unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */ 22 }; 23 24 static struct tty_audit_buf *tty_audit_buf_ref(void) 25 { 26 struct tty_audit_buf *buf; 27 28 buf = current->signal->tty_audit_buf; 29 WARN_ON(buf == ERR_PTR(-ESRCH)); 30 return buf; 31 } 32 33 static struct tty_audit_buf *tty_audit_buf_alloc(void) 34 { 35 struct tty_audit_buf *buf; 36 37 buf = kmalloc(sizeof(*buf), GFP_KERNEL); 38 if (!buf) 39 goto err; 40 buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); 41 if (!buf->data) 42 goto err_buf; 43 mutex_init(&buf->mutex); 44 buf->dev = MKDEV(0, 0); 45 buf->icanon = 0; 46 buf->valid = 0; 47 return buf; 48 49 err_buf: 50 kfree(buf); 51 err: 52 return NULL; 53 } 54 55 static void tty_audit_buf_free(struct tty_audit_buf *buf) 56 { 57 WARN_ON(buf->valid != 0); 58 kfree(buf->data); 59 kfree(buf); 60 } 61 62 static void tty_audit_log(const char *description, dev_t dev, 63 unsigned char *data, size_t size) 64 { 65 struct audit_buffer *ab; 66 struct task_struct *tsk = current; 67 pid_t pid = task_pid_nr(tsk); 68 uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); 69 uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); 70 unsigned int sessionid = audit_get_sessionid(tsk); 71 72 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); 73 if (ab) { 74 char name[sizeof(tsk->comm)]; 75 76 audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" 77 " minor=%d comm=", description, pid, uid, 78 loginuid, sessionid, MAJOR(dev), MINOR(dev)); 79 get_task_comm(name, tsk); 80 audit_log_untrustedstring(ab, name); 81 audit_log_format(ab, " data="); 82 audit_log_n_hex(ab, data, size); 83 audit_log_end(ab); 84 } 85 } 86 87 /** 88 * tty_audit_buf_push - Push buffered data out 89 * 90 * Generate an audit message from the contents of @buf, which is owned by 91 * the current task. @buf->mutex must be locked. 92 */ 93 static void tty_audit_buf_push(struct tty_audit_buf *buf) 94 { 95 if (buf->valid == 0) 96 return; 97 if (audit_enabled == 0) { 98 buf->valid = 0; 99 return; 100 } 101 tty_audit_log("tty", buf->dev, buf->data, buf->valid); 102 buf->valid = 0; 103 } 104 105 /** 106 * tty_audit_exit - Handle a task exit 107 * 108 * Make sure all buffered data is written out and deallocate the buffer. 109 * Only needs to be called if current->signal->tty_audit_buf != %NULL. 110 * 111 * The process is single-threaded at this point; no other threads share 112 * current->signal. 113 */ 114 void tty_audit_exit(void) 115 { 116 struct tty_audit_buf *buf; 117 118 buf = xchg(¤t->signal->tty_audit_buf, ERR_PTR(-ESRCH)); 119 if (!buf) 120 return; 121 122 tty_audit_buf_push(buf); 123 tty_audit_buf_free(buf); 124 } 125 126 /** 127 * tty_audit_fork - Copy TTY audit state for a new task 128 * 129 * Set up TTY audit state in @sig from current. @sig needs no locking. 130 */ 131 void tty_audit_fork(struct signal_struct *sig) 132 { 133 sig->audit_tty = current->signal->audit_tty; 134 } 135 136 /** 137 * tty_audit_tiocsti - Log TIOCSTI 138 */ 139 void tty_audit_tiocsti(struct tty_struct *tty, char ch) 140 { 141 dev_t dev; 142 143 dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; 144 if (tty_audit_push()) 145 return; 146 147 if (audit_enabled) 148 tty_audit_log("ioctl=TIOCSTI", dev, &ch, 1); 149 } 150 151 /** 152 * tty_audit_push - Flush current's pending audit data 153 * 154 * Returns 0 if success, -EPERM if tty audit is disabled 155 */ 156 int tty_audit_push(void) 157 { 158 struct tty_audit_buf *buf; 159 160 if (~current->signal->audit_tty & AUDIT_TTY_ENABLE) 161 return -EPERM; 162 163 buf = tty_audit_buf_ref(); 164 if (!IS_ERR_OR_NULL(buf)) { 165 mutex_lock(&buf->mutex); 166 tty_audit_buf_push(buf); 167 mutex_unlock(&buf->mutex); 168 } 169 return 0; 170 } 171 172 /** 173 * tty_audit_buf_get - Get an audit buffer. 174 * 175 * Get an audit buffer, allocate it if necessary. Return %NULL 176 * if out of memory or ERR_PTR(-ESRCH) if tty_audit_exit() has already 177 * occurred. Otherwise, return a new reference to the buffer. 178 */ 179 static struct tty_audit_buf *tty_audit_buf_get(void) 180 { 181 struct tty_audit_buf *buf; 182 183 buf = tty_audit_buf_ref(); 184 if (buf) 185 return buf; 186 187 buf = tty_audit_buf_alloc(); 188 if (buf == NULL) { 189 audit_log_lost("out of memory in TTY auditing"); 190 return NULL; 191 } 192 193 /* Race to use this buffer, free it if another wins */ 194 if (cmpxchg(¤t->signal->tty_audit_buf, NULL, buf) != NULL) 195 tty_audit_buf_free(buf); 196 return tty_audit_buf_ref(); 197 } 198 199 /** 200 * tty_audit_add_data - Add data for TTY auditing. 201 * 202 * Audit @data of @size from @tty, if necessary. 203 */ 204 void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size) 205 { 206 struct tty_audit_buf *buf; 207 unsigned int icanon = !!L_ICANON(tty); 208 unsigned int audit_tty; 209 dev_t dev; 210 211 audit_tty = READ_ONCE(current->signal->audit_tty); 212 if (~audit_tty & AUDIT_TTY_ENABLE) 213 return; 214 215 if (unlikely(size == 0)) 216 return; 217 218 if (tty->driver->type == TTY_DRIVER_TYPE_PTY 219 && tty->driver->subtype == PTY_TYPE_MASTER) 220 return; 221 222 if ((~audit_tty & AUDIT_TTY_LOG_PASSWD) && icanon && !L_ECHO(tty)) 223 return; 224 225 buf = tty_audit_buf_get(); 226 if (IS_ERR_OR_NULL(buf)) 227 return; 228 229 mutex_lock(&buf->mutex); 230 dev = MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; 231 if (buf->dev != dev || buf->icanon != icanon) { 232 tty_audit_buf_push(buf); 233 buf->dev = dev; 234 buf->icanon = icanon; 235 } 236 do { 237 size_t run; 238 239 run = N_TTY_BUF_SIZE - buf->valid; 240 if (run > size) 241 run = size; 242 memcpy(buf->data + buf->valid, data, run); 243 buf->valid += run; 244 data += run; 245 size -= run; 246 if (buf->valid == N_TTY_BUF_SIZE) 247 tty_audit_buf_push(buf); 248 } while (size != 0); 249 mutex_unlock(&buf->mutex); 250 } 251