/* * WPA module for netcfg * * Copyright (C) 2008 Glenn Saberton * * Licensed under the terms of the GNU General Public License version 2 * */ #include "netcfg.h" #include #include #include #include #ifdef WIRELESS #include "wpa_ctrl.h" #include pid_t wpa_supplicant_pid = -1; struct wpa_ctrl *ctrl; static int wpa_is_running = 0; int init_wpa_supplicant_support(struct netcfg_interface *interface) { if (access("/sbin/wpa_supplicant", F_OK) == 0) interface->wpa_supplicant_status = WPA_OK; else { interface->wpa_supplicant_status = WPA_UNAVAIL; di_info("Wpasupplicant not found on the system. Disabling WPA options"); } return 0; } int kill_wpa_supplicant(void) { pid_t wpa_pid; FILE *fp; fp = (fopen(WPAPID, "r")); if (fp == NULL) { di_warning("Couldn't read Wpasupplicant pid file, not trying to kill."); return 0; } else { if (fscanf(fp, "%d", &wpa_pid) != 1) { di_warning("Couldn't read pid from Wpasupplicant pid file, not trying to kill."); return 0; } fclose(fp); } if ((kill(wpa_pid, SIGTERM)) == 0) return 0; else { kill(wpa_pid, SIGKILL); unlink(WPAPID); return 0; } } int wireless_security_type (struct debconfclient *client, const char *if_name) { int ret = 0 ; debconf_subst(client, "netcfg/wireless_security_type", "iface", if_name); debconf_input(client, "high", "netcfg/wireless_security_type"); ret = debconf_go(client); if (ret == 30) return GO_BACK; debconf_get(client, "netcfg/wireless_security_type"); if (!strcmp(client->value, "wep/open")) return REPLY_WEP; else return REPLY_WPA; } int netcfg_set_passphrase (struct debconfclient *client, struct netcfg_interface *interface) { int ret; debconf_subst(client, "netcfg/wireless_wpa", "iface", interface->name); debconf_input(client, "high", "netcfg/wireless_wpa"); ret = debconf_go(client); if (ret == 30) return GO_BACK; if (interface->passphrase != NULL) free(interface->passphrase); debconf_get(client, "netcfg/wireless_wpa"); interface->passphrase = strdup(client->value); while (strlen(interface->passphrase) < WPA_MIN || strlen(interface->passphrase) > WPA_MAX) { debconf_subst(client, "netcfg/invalid_pass", "passphrase", interface->passphrase); debconf_input(client, "critical", "netcfg/invalid_pass"); debconf_go(client); free(interface->passphrase); debconf_input(client, "high", "netcfg/wireless_wpa"); ret = debconf_go(client); if (ret == 30) return GO_BACK; debconf_get(client, "netcfg/wireless_wpa"); interface->passphrase = strdup(client->value); } return 0; } static int start_wpa_daemon(struct debconfclient *client, const char *if_name) { wpa_supplicant_pid = fork(); if (wpa_supplicant_pid == 0) { fclose(client->out); if (execlp("wpa_supplicant", "wpa_supplicant", "-i", if_name, "-C", WPASUPP_CTRL, "-P", WPAPID, "-B", NULL) == -1) { di_error("could not exec wpasupplicant: %s", strerror(errno)); return 1; } else return 0; } else { waitpid(wpa_supplicant_pid, NULL, 0); return 0; } } void wpa_daemon_running(void) { FILE *fp = fopen(WPAPID, "r"); if (fp) { wpa_is_running = 1; fclose(fp); } } static int wpa_connect(const char *if_name) { char *cfile; int flen, res; flen = (strlen(WPASUPP_CTRL) + strlen(if_name) + 2); cfile = malloc(flen); if (cfile == NULL) { di_info("Can't allocate memory for WPA control interface."); return 1; } res = snprintf(cfile, flen, "%s/%s", WPASUPP_CTRL, if_name); if ((res < 0) || (res >= flen)) { free(cfile); return 1; } ctrl = wpa_ctrl_open(cfile); free(cfile); if (ctrl == NULL) { di_info("Couldn't connect to wpasupplicant"); return 1; } else return 0; } static int netcfg_wpa_cmd (char *cmd) { char buf[256]; size_t len; int ret; len = sizeof(buf) -1; ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL); if (ret < 0) { di_info("Sending %s to wpasupplicant failed", cmd); return 1; } return 0; } static int wpa_set_ssid (char *ssid) { int ret, res; size_t len; char cmd[256]; char buf[256]; res = snprintf(cmd, sizeof(cmd), "SET_NETWORK 0 %s \"%s\"", "ssid", ssid); if (res < 0) return 1; len = sizeof(buf) -1; ret = wpa_ctrl_request(ctrl, cmd, sizeof(cmd), buf, &len, NULL); if (ret != 0) { di_info("Failed to set the ssid with wpasupplicant"); return 1; } return 0; } static int wpa_set_psk(char *passphrase) { int ret, res; size_t len; char buf[256]; char cmd[256]; res = snprintf(cmd, sizeof(cmd), "SET_NETWORK 0 %s \"%s\"", "psk", passphrase); if (res < 0) return 1; len = sizeof(buf) -1; ret = wpa_ctrl_request(ctrl, cmd, sizeof(cmd), buf, &len, NULL); if (ret != 0) return 1; return 0; } static int wpa_status(void) { int ret; size_t len; char buf[2048]; const char *success = "wpa_state=COMPLETED"; len = sizeof(buf) -1; ret = wpa_ctrl_request(ctrl, "STATUS", 7, buf, &len, NULL); if (ret == 0) { buf[len] = '\0'; di_info("buf = %s", buf); } else return 1; if (strstr(buf, success) == NULL) return 1; else { di_info("success"); return 0; } } int poll_wpa_supplicant(struct debconfclient *client) { int wpa_timeout = 60; int seconds_slept = 0; int state = 1; debconf_capb(client, "backup progresscancel"); debconf_progress_start(client, 0, wpa_timeout, "netcfg/wpa_progress"); for (seconds_slept = 0; seconds_slept <= wpa_timeout; seconds_slept++) { if (debconf_progress_info(client, "netcfg/wpa_progress_note") == 30) goto stop; if (debconf_progress_step(client, 1) == 30) goto stop; sleep(1); if ((seconds_slept <= wpa_timeout) && (seconds_slept % 5) == 0) { if (!wpa_status()) { debconf_progress_set(client, wpa_timeout); debconf_progress_info(client, "netcfg/wpa_success_note"); state = 0; sleep(2); goto stop; } } if (seconds_slept == wpa_timeout) { debconf_progress_stop(client); debconf_capb(client, "backup"); debconf_capb(client, ""); debconf_input(client, "critical", "netcfg/wpa_supplicant_failed"); debconf_go(client); debconf_capb(client, "backup"); return 1; } } stop: debconf_progress_stop(client); debconf_capb(client, "backup"); if (!state) return 0; else return 1; } int wpa_supplicant_start(struct debconfclient *client, const struct netcfg_interface *interface) { int retry = 0; enum { CHECK_DAEMON, START_DAEMON, CONNECT, PING, ADD_NETWORK, SET_ESSID, SET_PSK, SET_SCAN_SSID, ENABLE_NETWORK, POLL, ABORT, SUCCESS } state = CHECK_DAEMON; for (;;) { switch(state) { case CHECK_DAEMON: wpa_daemon_running(); if (wpa_is_running) state = CONNECT; else state = START_DAEMON; break; case START_DAEMON: if (!start_wpa_daemon(client, interface->name)) state = CONNECT; else state = ABORT; break; case CONNECT: if (wpa_connect(interface->name) == 0) state = PING; else state = ABORT; break; case PING: /* if the daemon doesn't respond, try and ping * it and increment retry. If we have done * this 4 times, something must be wrong * so bail out. */ retry++; if (retry > 4) state = ABORT; else if (netcfg_wpa_cmd("PING")) { kill_wpa_supplicant(); state = START_DAEMON; break; } else state = ADD_NETWORK; break; case ADD_NETWORK: if (wpa_is_running) { state = SET_ESSID; break; } if (netcfg_wpa_cmd("ADD_NETWORK")) state = PING; else state = SET_ESSID; break; case SET_ESSID: if (wpa_set_ssid(interface->essid)) state = PING; else state = SET_PSK; break; case SET_PSK: if (wpa_set_psk(interface->passphrase)) state = PING; else state = SET_SCAN_SSID; break; case SET_SCAN_SSID: if (netcfg_wpa_cmd("SET_NETWORK 0 scan_ssid 1")) state = PING; else state = ENABLE_NETWORK; break; case ENABLE_NETWORK: if (netcfg_wpa_cmd("ENABLE_NETWORK 0")) state = PING; else state = POLL; break; case POLL: if (poll_wpa_supplicant(client)) state = ABORT; else state = SUCCESS; break; case ABORT: if (ctrl == NULL) return GO_BACK; else { wpa_ctrl_close(ctrl); ctrl = NULL; return GO_BACK; } case SUCCESS: if (ctrl == NULL) return 0; else { wpa_ctrl_close(ctrl); ctrl = NULL; return 0; } } } } #else /* Non-WIRELESS stubs of public API */ int init_wpa_supplicant_support(struct netcfg_interface *interface) { (void)interface; return 0; } int kill_wpa_supplicant(void) { return 0; } int wireless_security_type(struct debconfclient *client, const char *if_name) { (void)client; (void)if_name; return 0; } int netcfg_set_passphrase(struct debconfclient *client, struct netcfg_interface *interface) { (void)client; (void)interface; return 0; } int wpa_supplicant_start(struct debconfclient *client, const struct netcfg_interface *interface) { (void)client; (void)interface; return 0; } #endif /* WIRELESS */