/*
* WPA module for netcfg
*
* Copyright (C) 2008 Glenn Saberton <[email protected]>
*
* Licensed under the terms of the GNU General Public License version 2
*
*/
#include "netcfg.h"
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <debian-installer.h>
#ifdef WIRELESS
#include "wpa_ctrl.h"
#include <iwlib.h>
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)
{
debconf_subst(client, "netcfg/wireless_security_type", "iface", if_name);
debconf_input(client, "high", "netcfg/wireless_security_type");
if (debconf_go(client) == CMD_GOBACK)
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)
{
debconf_subst(client, "netcfg/wireless_wpa", "iface", interface->name);
debconf_input(client, "high", "netcfg/wireless_wpa");
if (debconf_go(client) == CMD_GOBACK)
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");
if (debconf_go(client) == CMD_GOBACK)
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") ==
CMD_PROGRESSCANCELLED)
goto stop;
if (debconf_progress_step(client, 1) == CMD_PROGRESSCANCELLED)
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 */