summaryrefslogtreecommitdiffstats
path: root/src/aeswepd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/aeswepd.c')
-rw-r--r--src/aeswepd.c587
1 files changed, 587 insertions, 0 deletions
diff --git a/src/aeswepd.c b/src/aeswepd.c
new file mode 100644
index 0000000..9b4c07f
--- /dev/null
+++ b/src/aeswepd.c
@@ -0,0 +1,587 @@
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <sys/select.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <libdaemon/dlog.h>
+#include <libdaemon/dpid.h>
+#include <libdaemon/dsignal.h>
+#include <libdaemon/dfork.h>
+
+#include "aes.h"
+#include "aeswepd.h"
+#include "iwkey.h"
+#include "interface.h"
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef VARLIBAESWEPD
+#define VARLIBAESWEPD "/var/lib/aeswepd"
+#endif
+
+uint8_t aes_key[AES_KEY_LEN];
+int rekey_time = 15*60;
+int n_max_keys = 3;
+int key_map[MAX_WEP_KEYS];
+char *interface_name = NULL;
+int daemonize = 1, use_syslog = 1, wait_on_fork = 0, use_status_file = 1;
+char log_ident[32], pid_ident[32];
+
+FILE *status_file = NULL;
+
+const char *get_status_file_name(void) {
+ static int init = 0;
+ static char path[PATH_MAX];
+
+ if (!init) {
+ snprintf(path, sizeof(path), "%s/status.%s", VARLIBAESWEPD, interface_name);
+ init = 1;
+ }
+
+ return path;
+}
+
+
+int do_list_keys() {
+ FILE *f;
+ char ln[256];
+
+ if (!(f = fopen(get_status_file_name(), "r"))) {
+ daemon_log(LOG_ERR, "Failed to open '%s': %s", get_status_file_name(), strerror(errno));
+ return -1;
+ }
+
+ while (fgets(ln, sizeof(ln), f))
+ fputs(ln, stdout);
+
+ fclose(f);
+ return 0;
+}
+
+int wep_key_calc(uint32_t t, uint8_t w[WEP_KEY_LEN]) {
+ uint8_t data[AES_KEY_LEN], result[AES_KEY_LEN];
+ uint32_t v = t;
+ int i, shift;
+
+ for (i = 0, shift = 0; i < AES_KEY_LEN; i++) {
+ data[i] = (uint8_t) (v >> shift);
+
+ if ((shift += 8) >= sizeof(v)*8)
+ shift = 0;
+ }
+
+ if (aes_crypt(aes_key, data, result) < 0) {
+ daemon_log(LOG_ERR, "aes_crypt() failed!");
+ return -1;
+ }
+
+ memset(w, 0, WEP_KEY_LEN);
+ memcpy(w, result, MIN(WEP_KEY_LEN, AES_KEY_LEN));
+
+ return 0;
+}
+
+int print_wep_key(FILE *f, int t, uint8_t wep[WEP_KEY_LEN]) {
+ time_t rt = t*rekey_time;
+ fprintf(f, "%10i: Starting %s\t WEP: ", t, ctime(&rt));
+ print_hex(f, wep, WEP_KEY_LEN);
+ fprintf(f, "\n");
+ return 0;
+}
+
+
+int rekey(struct interface *i, time_t now, time_t *next_rekey) {
+ uint8_t w[WEP_KEY_LEN];
+ uint32_t t;
+
+ assert(i && n_max_keys >= 1 && next_rekey);
+
+ t = now/rekey_time;
+
+ daemon_log(LOG_ERR, "Rekeying for %i", t);
+
+ if (status_file) {
+ rewind(status_file);
+ ftruncate(fileno(status_file), 0);
+ }
+
+ if (wep_key_calc(t, w) < 0)
+ return -1;
+
+ if (wep_key_add(i, w) < 0)
+ return -1;
+
+ if (status_file)
+ print_wep_key(status_file, t, w);
+
+ if (n_max_keys >= 3) {
+ if (wep_key_calc(t-1, w) < 0)
+ return -1;
+
+ if (wep_key_add(i, w) < 0)
+ return -1;
+
+ if (status_file)
+ print_wep_key(status_file, t-1, w);
+
+ if (wep_key_calc(t+1, w) < 0)
+ return -1;
+
+ if (wep_key_add(i, w) < 0)
+ return -1;
+
+ if (status_file)
+ print_wep_key(status_file, t+1, w);
+ }
+
+ if (status_file)
+ fflush(status_file);
+
+ if (wep_key_finish(i) < 0)
+ return -1;
+
+ *next_rekey = (t+1)*rekey_time;
+
+ return 0;
+}
+
+int go(struct interface *i) {
+ time_t next_rekey = 0;
+ int send_retval = 1;
+ fd_set fds;
+ time_t now = time(NULL);
+ int sigfd, r = -1;
+
+ daemon_log(LOG_INFO, "aeswepd "VERSION" initializing. (rekey_time=%i)", rekey_time);
+
+ if (daemon_pid_file_create() < 0) {
+ daemon_log(LOG_ERR, "Could not create PID file %s.", daemon_pid_file_proc());
+ goto finish;
+ }
+
+ if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, -1) < 0) {
+ daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno));
+ goto finish;
+ }
+
+ if (use_status_file) {
+ mode_t u;
+
+ u = umask(077);
+ status_file = fopen(get_status_file_name(), "w");
+ umask(u);
+
+ if (!status_file) {
+ daemon_log(LOG_WARNING, "Failed to open status file '%s', doing without: %s", get_status_file_name(), strerror(errno));
+ use_status_file = 0;
+ }
+ }
+
+ if (rekey(i, now, &next_rekey) < 0)
+ goto finish;
+
+ daemon_log(LOG_INFO, "aeswepd successfully initialized.");
+
+ if (daemonize && wait_on_fork) {
+ daemon_retval_send(0);
+ send_retval = 0;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(sigfd = daemon_signal_fd(), &fds);
+
+ for (;;) {
+ fd_set qfds;
+ struct timeval tv;
+ now = time(NULL);
+
+ if (next_rekey < now) {
+ if (rekey(i, now, &next_rekey) < 0)
+ return -1;
+ }
+
+ qfds = fds;
+
+ now = time(NULL);
+ tv.tv_sec = next_rekey > now ? next_rekey - now : 0;
+ tv.tv_usec = 0;
+
+ if (select(sigfd+1, &qfds, NULL, NULL, &tv) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ daemon_log(LOG_ERR, "select() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+
+ if (FD_ISSET(sigfd, &qfds)) {
+ int sig;
+
+ if ((sig = daemon_signal_next()) < 0) {
+ daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno));
+ goto finish;
+ }
+
+
+ switch (sig) {
+
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ r = 0;
+ goto finish;
+
+ case SIGHUP:
+ next_rekey = 0;
+ break;
+
+ default:
+ daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig));
+ break;
+
+ }
+ }
+ }
+
+ r = 0;
+
+finish:
+
+ if (status_file) {
+ fclose(status_file);
+ unlink(get_status_file_name());
+ }
+
+ if (send_retval && daemonize && wait_on_fork)
+ daemon_retval_send(1);
+
+ daemon_pid_file_remove();
+ daemon_signal_done();
+
+ daemon_log(LOG_INFO, "Exiting.");
+
+ return r;
+}
+
+void usage(char *p) {
+ if (strrchr(p, '/'))
+ p = strchr(p, '/')+1;
+
+ printf("%s -- AES Rekeying Daemon for IEEE 802.11b WEP\n\n"
+ "Usage: %s [options]\n\n"
+ "Options:\n"
+ " -n --no-daemon Do not daemonize (for debugging) (%s)\n"
+ " -s --no-syslog Do not use syslog, use stderr instead (for debugging) (%s)\n"
+ " -i --iface=IFACE Specify network interface (%s)\n"
+ " -t --rekey-time=SECS Specify rekeying interval in seconds (%i)\n"
+ " -K --key=KEY Specify AES key (don't use this!)\n"
+ " -F --key-file=KEYFILE File to read key from\n"
+ " -E --key-env=KEYENV Specify environment variable to read key from\n"
+ " -m --max-keys=NR Specify the number of keys to use concurrently (%i)\n"
+ " -w --wait-on-fork Wait until daemon fork finished (%s)\n"
+ " -l --list-keys Show keys of running daemon\n"
+ " -h --help Show this help\n"
+ " -k --kill Kill a running daemon\n"
+ " -c --check-running Check if a daemon is currently running\n"
+ " -v --version Show version\n\n"
+ " <KEY>: A %i byte AES key as hexadecimal string\n",
+ p, p,
+ !daemonize ? "on" : "off",
+ !use_syslog ? "on" : "off",
+ interface_name,
+ rekey_time,
+ n_max_keys,
+ wait_on_fork ? "on" : "off",
+ AES_KEY_LEN);
+}
+
+void parse_args(int argc, char *argv[]) {
+ static struct option long_options[] = {
+ {"no-daemon", no_argument, 0, 'n'},
+ {"no-syslog", no_argument, 0, 's'},
+ {"iface", required_argument, 0, 'i'},
+ {"rekey-time", required_argument, 0, 't'},
+ {"max-keys", required_argument, 0, 'm'},
+ {"wait-on-fork", no_argument, 0, 'w'},
+ {"help", no_argument, 0, 'h'},
+ {"kill", no_argument, 0, 'k'},
+ {"check-running", no_argument, 0, 'c'},
+ {"version", no_argument, 0, 'v'},
+ {"list-keys", no_argument, 0, 'l'},
+ {"key", no_argument, 0, 'K'},
+ {"key-file", no_argument, 0, 'F'},
+ {"key-env", no_argument, 0, 'E'},
+ {0, 0, 0, 0}
+ };
+ int option_index = 0;
+ int _help = 0, _kill = 0, _check = 0, _version = 0, r, _list_keys = 0, _key_set = 0;
+ char ln[64];
+ char *b;
+
+ if ((b = strrchr(argv[0], '/')))
+ b++;
+ else
+ b = argv[0];
+
+
+ if (strcmp(b, "aeswepl") == 0)
+ _list_keys = 1;
+
+ memset(aes_key, sizeof(aes_key), 0);
+
+ for (;;) {
+ int c;
+
+ if ((c = getopt_long(argc, argv, "nsi:t:m:whkcvlK:F:E:", long_options, &option_index)) < 0)
+ break;
+
+ switch (c) {
+ case 'n' :
+ daemonize = !daemonize;
+ break;
+ case 's' :
+ use_syslog = !use_syslog;
+ break;
+ case 'i' :
+ if (interface_name)
+ free(interface_name);
+
+ interface_name = strdup(optarg);
+ break;
+ case 't':
+ if ((rekey_time = atoi(optarg)) <= 0) {
+ fprintf(stderr, "Rekey time too short.\n");
+ exit(1);
+ }
+ break;
+ case 'm':
+ n_max_keys = atoi(optarg);
+ if (n_max_keys <= 0 || n_max_keys > 4) {
+ fprintf(stderr, "--max-keys has to be between 1 and 4\n");
+ exit(1);
+ }
+ break;
+ case 'w':
+ wait_on_fork = !wait_on_fork;
+ break;
+ case 'h':
+ _help = 1;
+ break;
+ case 'k':
+ _kill = 1;
+ break;
+ case 'c':
+ _check = 1;
+ break;
+ case 'v':
+ _version = 1;
+ break;
+ case 'l':
+ _list_keys = 1;
+ break;
+
+ case 'K':
+ daemon_log(LOG_WARNING, "WARNING: Don't use the --key option, other local users might peek on 'ps'. Proceeding");
+ strncpy(ln, optarg, sizeof(ln)-1);
+ ln[sizeof(ln)-1] = 0;
+ _key_set = 1;
+ memset(optarg, 'X', strlen(optarg));
+ break;
+
+ case 'E':
+ if (!getenv(optarg)) {
+ daemon_log(LOG_ERR, "Environment variable for key '%s' not existent.", optarg);
+ exit(1);
+ }
+
+ strncpy(ln, getenv(optarg), sizeof(ln)-1);
+ ln[sizeof(ln)-1] = 0;
+ _key_set = 1;
+ unsetenv(optarg);
+ break;
+
+ case 'F':{
+ FILE *f;
+ struct stat st;
+
+ if (!(f = fopen(optarg, "r"))) {
+ daemon_log(LOG_ERR, "Could not open key file '%s': %s", optarg, strerror(errno));
+ exit(1);
+ }
+
+ if (fstat(fileno(f), &st) < 0) {
+ daemon_log(LOG_ERR, "Failed to stat file '%s': %s", optarg, strerror(errno));
+ exit(1);
+ }
+
+ if (st.st_mode & 077 || st.st_uid != 0) {
+ daemon_log(LOG_ERR, "Key file '%s' must be owned by root and have an access mode of 0700 or less.", optarg);
+ exit(1);
+ }
+
+ ln[0] = 0;
+ fgets(ln, sizeof(ln), f);
+ fclose(f);
+ ln[strcspn(ln, " \n\r\t")] = 0;
+ _key_set = 1;
+ break;
+ }
+
+ default:
+ fprintf(stderr, "Unknown parameter.\n");
+ exit(1);
+ }
+ }
+
+ if (!interface_name)
+ interface_name = strdup("wlan0");
+
+ snprintf(pid_ident, sizeof(pid_ident), "aeswepd.%s", interface_name);
+ daemon_pid_file_ident = pid_ident;
+ snprintf(log_ident, sizeof(log_ident), "aeswepd(%s)", interface_name);
+ daemon_log_ident = log_ident;
+
+
+ if (_help) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ if (_kill) {
+ if (daemon_pid_file_kill(SIGINT) < 0) {
+ daemon_log(LOG_ERR, "Failed to kill daemon. (%s)", strerror(errno));
+ exit(6);
+ }
+
+ exit(0);
+ }
+
+ if (_version) {
+ printf("aeswepd "VERSION"\n");
+ exit(0);
+ }
+
+ if (_check) {
+ pid_t pid = daemon_pid_file_is_running();
+
+ if (pid == (pid_t) -1)
+ printf("aeswepd not running.\n");
+ else
+ printf("aeswepd process for device %s running as pid %u.\n", interface_name, pid);
+
+ exit(pid == 0 ? 255 : 0);
+ }
+
+ if (_list_keys) {
+ if (daemon_pid_file_is_running() == (pid_t) -1) {
+ daemon_log(LOG_ERR, "Daemon not running!");
+ exit(1);
+ }
+
+ do_list_keys();
+ exit(0);
+ }
+
+ if (!_key_set) {
+ daemon_log(LOG_ERR, "Not AES key specified!");
+ exit(1);
+ }
+
+ if ((r = parse_hex(ln, aes_key, sizeof(aes_key))) < 0) {
+ daemon_log(LOG_ERR, "Failed to parse AES key at position %i!", -r-1);
+ exit(1);
+ }
+
+ if (r != 16) {
+ daemon_log(LOG_ERR, "AES key to short: 16 bytes required, %i bytes read", r);
+ exit(1);
+ }
+
+ if (!use_syslog)
+ daemon_log_use = DAEMON_LOG_STDERR;
+}
+
+int main(int argc, char *argv[]) {
+ struct interface *i = NULL;
+ int r = 1, j;
+ pid_t pid;
+
+ daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
+
+ parse_args(argc, argv);
+
+ if (geteuid() != 0) {
+ daemon_log(LOG_ERR, "Sorry, you need to be root to run this binary.");
+ goto finish;
+ }
+
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
+ goto finish;
+
+ }
+
+ if (daemonize) {
+ pid_t pid;
+
+ if (wait_on_fork)
+ if (daemon_retval_init() < 0) {
+ daemon_log(LOG_ERR, "Sorry, could not create pipe: %s", strerror(errno));
+ goto finish;
+ }
+
+ if ((pid = daemon_fork()) < 0)
+ goto finish;
+
+ if (pid) {
+ int c = 0;
+
+ // Parent process
+
+ if (wait_on_fork)
+ if ((c = daemon_retval_wait(60)) < 0) {
+ daemon_log(LOG_WARNING, "Killing background process.");
+ kill(pid, SIGTERM);
+ }
+
+ r = c;
+ goto finish;
+ }
+ }
+
+
+ for (j = 0; j < MAX_WEP_KEYS; j++)
+ key_map[j] = j;
+
+ if (!(i = interface_open(interface_name)) < 0)
+ goto finish;
+
+ if (go(i) < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+
+ if (i)
+ interface_close(i);
+
+ if (interface_name)
+ free(interface_name);
+
+ return r;
+}