/* $Id$ */ /* * This file is part of aeswepd. * * aeswepd 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 2 of the License, or * (at your option) any later version. * * aeswepd 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 aeswepd; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aes.h" #include "aeswepd.h" #include "util.h" #include "keyapi.h" uint8_t aes_key[AES_KEY_LEN]; uint32_t rekey_time = 24*60*60; /* prior to 0.4: 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, wait_on_kill = 0; 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(void) { 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(const struct key_api *ka, void *c, time_t now, time_t *next_rekey) { uint8_t w[WEP_KEY_LEN]; uint32_t t; assert(n_max_keys >= 1 && next_rekey && ka); 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 (ka->add(c, 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 (ka->add(c, 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 (ka->add(c, w) < 0) return -1; if (status_file) print_wep_key(status_file, t+1, w); } if (status_file) fflush(status_file); if (ka->finish(c) < 0) return -1; *next_rekey = (t+1)*rekey_time; return 0; } int go(void) { time_t next_rekey = 0; int send_retval = 1; fd_set fds; time_t now = time(NULL); int sigfd, r = -1; void *c = NULL; const struct key_api *ka = NULL; 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 (!(ka = key_api_get(interface_name))) { daemon_log(LOG_ERR, "Failed to find key API for specified interface %s", interface_name); goto finish; } if (!(c = ka->open(interface_name))) goto finish; if (rekey(ka, c, 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(ka, c, 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 (c && ka) { ka->close(c); c = NULL; } 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.11 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" " -W --wait-on-kill When run with -k, wait until the daemon died (%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", wait_on_kill ? "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'}, {"wait-on-kill", 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, _list_keys = 0, _key_set = 0, r; char ln[64]; char *b; if ((b = strrchr(argv[0], '/'))) b++; else b = argv[0]; if (strcmp(b, "aeswepls") == 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:W", 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) { daemon_log(LOG_ERR, "Rekey time too short.\n"); exit(1); } break; case 'm': n_max_keys = atoi(optarg); if (n_max_keys <= 0 || n_max_keys > 4) { daemon_log(LOG_ERR, "--max-keys has to be between 1 and 4\n"); exit(1); } break; case 'w': wait_on_fork = !wait_on_fork; break; case 'W': wait_on_kill = !wait_on_kill; 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 anyway."); 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: daemon_log(LOG_ERR, "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) { int rv; if (wait_on_kill) rv = daemon_pid_file_kill_wait(SIGINT, 5); else rv = daemon_pid_file_kill(SIGINT); if (rv < 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 || pid == 0) { printf("aeswepd not running.\n"); exit(255); } else { printf("aeswepd process for device %s running as pid %u.\n", interface_name, pid); exit(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[]) { 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) { // Parent process if (wait_on_fork) { if ((r = daemon_retval_wait(60)) < 0) { daemon_log(LOG_WARNING, "Killing background process."); kill(pid, SIGTERM); r = 1; } } else r = 0; goto finish; } } for (j = 0; j < MAX_WEP_KEYS; j++) key_map[j] = j; if (go() < 0) goto finish; r = 0; finish: if (interface_name) free(interface_name); return r; }