From 3e81b0123b4bbfedbdc1135a6a4305c347f91a3a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 9 Aug 2003 16:41:59 +0000 Subject: Initial commit git-svn-id: file:///home/lennart/svn/public/aeswepd/trunk@3 022f378f-78c4-0310-b860-d162c87e6274 --- src/aeswepd.c | 587 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 587 insertions(+) create mode 100644 src/aeswepd.c (limited to 'src/aeswepd.c') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "aes.h" +#include "aeswepd.h" +#include "iwkey.h" +#include "interface.h" +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include +#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" + " : 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; +} -- cgit