/* Copyright 2010 Adam Marchetti Copyright 2011-2012 Timothy Pearson This file is part of tsak, the TDE Secure Attention Key daemon tsak is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. tsak is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with tsak. If not, see http://www.gnu.org/licenses/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FIFO_DIR "/tmp/ksocket-global" #define FIFO_FILE_OUT "/tmp/ksocket-global/tsak" #define FIFO_LOCKFILE_OUT "/tmp/ksocket-global/tsak.lock" #define MAX_KEYBOARDS 64 #define MAX_INPUT_NODE 128 #define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8))) typedef unsigned char byte; bool mPipeOpen_out = false; int mPipe_fd_out = -1; int mPipe_lockfd_out = -1; char filename[32]; char key_bitmask[(KEY_MAX + 7) / 8]; struct sigaction usr_action; sigset_t block_mask; int keyboard_fd_num; int keyboard_fds[MAX_KEYBOARDS]; int child_pids[MAX_KEYBOARDS]; const char *keycode[256] = { "", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "−", "=", "", "", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\n", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "", "", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "", "", "", " ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "\\", "f11", "f12", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }; /* returns 1 if bit number i is set, otherwise returns 0 */ int bit_set(size_t i, const byte* a) { return a[i/CHAR_BIT] & (1 << i%CHAR_BIT); } // -------------------------------------------------------------------------------------- // Useful function from Stack Overflow // http://stackoverflow.com/questions/874134/find-if-string-endswith-another-string-in-c // -------------------------------------------------------------------------------------- /* returns 1 iff str ends with suffix */ int str_ends_with(const char * str, const char * suffix) { if( str == NULL || suffix == NULL ) return 0; size_t str_len = strlen(str); size_t suffix_len = strlen(suffix); if(suffix_len > str_len) return 0; return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len ); } // -------------------------------------------------------------------------------------- /* Assign features (supported axes and keys) of the physical input device (devin) * to the virtual input device (devout) */ static void copy_features(int devin, int devout) { byte evtypes[EV_MAX/CHAR_BIT + 1] = {0}; byte codes[KEY_MAX/CHAR_BIT + 1]; unsigned i,code; int op; if (ioctl(devin, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) return; for(i=0;i= 0) { for(code=0;code -1) { if (fl.l_type == F_WRLCK) { return false; } return true; } return true; } bool setupPipe() { /* Create the FIFOs if they do not exist */ umask(0); mkdir(FIFO_DIR,0644); mknod(FIFO_FILE_OUT, S_IFIFO|0600, 0); chmod(FIFO_FILE_OUT, 0600); mPipe_fd_out = open(FIFO_FILE_OUT, O_RDWR | O_NONBLOCK); if (mPipe_fd_out > -1) { mPipeOpen_out = true; } // Set the exclusive file lock return setFileLock(mPipe_fd_out, true); } bool setupLockingPipe() { /* Create the FIFOs if they do not exist */ umask(0); mkdir(FIFO_DIR,0644); mknod(FIFO_LOCKFILE_OUT, S_IFIFO|0600, 0); chmod(FIFO_LOCKFILE_OUT, 0600); mPipe_lockfd_out = open(FIFO_LOCKFILE_OUT, O_RDWR | O_NONBLOCK); if (mPipe_lockfd_out > -1) { // Set the exclusive file lock return setFileLock(mPipe_lockfd_out, true); } return false; } void broadcast_sak() { // Let anyone listening to our interface know that an SAK keypress was received // I highly doubt there are more than 255 VTs active at once... int i; for (i=0;i<255;i++) { if (write(mPipe_fd_out, "SAK\n\r", 6) < 0) { fprintf(stderr, "Unable to send SAK signal to clients\n"); } } } void restart_tsak() { int i; fprintf(stderr, "Forcibly terminating...\n"); // Close down all child processes for (i=0; i0) { return 4; } sleep(1); restart_tsak(); } } else { fprintf(stderr, "Found %d keyboard(s)\n", keyboard_fd_num); can_proceed = true; for (current_keyboard=0;current_keyboard0) { child_pids[current_keyboard] = i; continue; } setupLockingPipe(); } established = true; if (testrun == true) { return 0; } while (1) { if ((rd = read (keyboard_fds[current_keyboard], ev, size * 2)) < size) { fprintf(stderr, "Read failed.\n"); break; } // Replicate LED events from the virtual keyboard to the physical keyboard int rrd = read(devout[current_keyboard], &revev, size); if (rrd >= size) { if (revev.type == EV_LED) { if (write(keyboard_fds[current_keyboard], &revev, sizeof(revev)) < 0) { fprintf(stderr, "Unable to replicate LED event\n"); } } } value = ev[0].value; if (value != ' ' && ev[1].value == 0 && ev[1].type == 1){ // Read the key release event if (keycode[(ev[1].code)]) { if (strcmp(keycode[(ev[1].code)], "") == 0) ctrl_down = false; if (strcmp(keycode[(ev[1].code)], "") == 0) alt_down = false; } } if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){ // Read the key press event if (keycode[(ev[1].code)]) { if (strcmp(keycode[(ev[1].code)], "") == 0) ctrl_down = true; if (strcmp(keycode[(ev[1].code)], "") == 0) alt_down = true; } } hide_event = false; if (keycode[(ev[1].code)]) { if (alt_down && ctrl_down && (strcmp(keycode[(ev[1].code)], "") == 0)) { hide_event = true; } } if ((hide_event == false) && (ev[0].type != EV_LED) && (ev[1].type != EV_LED)) { // Pass the event on... event = ev[0]; if (write(devout[current_keyboard], &event, sizeof event) < 0) { fprintf(stderr, "Unable to replicate keyboard event!\n"); } event = ev[1]; if (write(devout[current_keyboard], &event, sizeof event) < 0) { fprintf(stderr, "Unable to replicate keyboard event!\n"); } } if (hide_event == true) { // Let anyone listening to our interface know that an SAK keypress was received broadcast_sak(); } } } } } // fork udev monitor process int i=fork(); if (i<0) return 10; // fork failed if (i>0) { // Terminate parent return 0; } // Prevent multiple process instances from starting setupLockingPipe(); // Wait a little bit so that udev hotplug can stabilize before we start monitoring sleep(1); fprintf(stderr, "Hotplug monitoring process started\n"); // Monitor for hotplugged keyboards int j; int hotplug_fd; bool is_new_keyboard; struct udev *udev; struct udev_device *dev; struct udev_monitor *mon; // Create the udev object udev = udev_new(); if (!udev) { fprintf(stderr, "Cannot connect to udev interface\n"); return 11; } // Set up a udev monitor to monitor input devices mon = udev_monitor_new_from_netlink(udev, "udev"); udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL); udev_monitor_enable_receiving(mon); while (1) { // Watch for input from the monitoring process dev = udev_monitor_receive_device(mon); if (dev) { // If a keyboard was removed we need to restart... if (strcmp(udev_device_get_action(dev), "remove") == 0) { udev_device_unref(dev); udev_unref(udev); restart_tsak(); } is_new_keyboard = false; snprintf(filename,sizeof(filename), "%s", udev_device_get_devnode(dev)); udev_device_unref(dev); // Print name of keyboard hotplug_fd = open(filename, O_RDWR|O_SYNC); ioctl(hotplug_fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); /* We assume that anything that has an alphabetic key in the QWERTYUIOP range in it is the main keyboard. */ for (j = KEY_Q; j <= KEY_P; j++) { if (TestBit(j, key_bitmask)) { is_new_keyboard = true; } } ioctl (hotplug_fd, EVIOCGNAME (sizeof (name)), name); close(hotplug_fd); // Ensure that we do not detect our own tsak faked keyboards if (str_ends_with(name, "+tsak") == 1) { is_new_keyboard = false; } // If a keyboard was added we need to restart... if (is_new_keyboard == true) { fprintf(stderr, "Hotplugged new keyboard: (%s)\n", name); udev_unref(udev); restart_tsak(); } } else { fprintf(stderr, "No Device from receive_device(). An error occured.\n"); } } udev_unref(udev); fprintf(stderr, "Hotplug monitoring process terminated\n"); } } } return 6; }