Allow PSK/passphrase to be set only when needed

The new network profile parameter mem_only_psk=1 can be used to specify
that the PSK/passphrase for that network is requested over the control
interface (ctrl_iface or D-Bus) similarly to the EAP network parameter
requests. The PSK/passphrase can then be configured temporarily in a way
that prevents it from getting stored to the configuration file.

For example:

Event:
CTRL-REQ-PSK_PASSPHRASE-0:PSK or passphrase needed for SSID test-wpa2-psk

Response:
CTRL-RSP-PSK_PASSPHRASE-0:"qwertyuiop"

Note: The response value uses the same encoding as the psk network
profile parameter, i.e., passphrase is within double quotation marks.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2015-03-28 11:05:13 +02:00
parent b898a6ee72
commit a52410c29f
9 changed files with 109 additions and 27 deletions

View file

@ -310,6 +310,7 @@ enum wpa_ctrl_req_type {
WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_OTP,
WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_EAP_PASSPHRASE,
WPA_CTRL_REQ_SIM, WPA_CTRL_REQ_SIM,
WPA_CTRL_REQ_PSK_PASSPHRASE,
NUM_WPA_CTRL_REQS NUM_WPA_CTRL_REQS
}; };

View file

@ -1816,6 +1816,7 @@ static const struct parse_data ssid_fields[] = {
{ FUNC(bssid_blacklist) }, { FUNC(bssid_blacklist) },
{ FUNC(bssid_whitelist) }, { FUNC(bssid_whitelist) },
{ FUNC_KEY(psk) }, { FUNC_KEY(psk) },
{ INT(mem_only_psk) },
{ FUNC(proto) }, { FUNC(proto) },
{ FUNC(key_mgmt) }, { FUNC(key_mgmt) },
{ INT(bg_scan_period) }, { INT(bg_scan_period) },

View file

@ -501,7 +501,12 @@ static void write_bssid(FILE *f, struct wpa_ssid *ssid)
static void write_psk(FILE *f, struct wpa_ssid *ssid) static void write_psk(FILE *f, struct wpa_ssid *ssid)
{ {
char *value = wpa_config_get(ssid, "psk"); char *value;
if (ssid->mem_only_psk)
return;
value = wpa_config_get(ssid, "psk");
if (value == NULL) if (value == NULL)
return; return;
fprintf(f, "\tpsk=%s\n", value); fprintf(f, "\tpsk=%s\n", value);
@ -673,6 +678,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
write_str(f, "bssid_blacklist", ssid); write_str(f, "bssid_blacklist", ssid);
write_str(f, "bssid_whitelist", ssid); write_str(f, "bssid_whitelist", ssid);
write_psk(f, ssid); write_psk(f, ssid);
INT(mem_only_psk);
write_proto(f, ssid); write_proto(f, ssid);
write_key_mgmt(f, ssid); write_key_mgmt(f, ssid);
INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);

View file

@ -180,6 +180,14 @@ struct wpa_ssid {
*/ */
char *ext_psk; char *ext_psk;
/**
* mem_only_psk - Whether to keep PSK/passphrase only in memory
*
* 0 = allow psk/passphrase to be stored to the configuration file
* 1 = do not store psk/passphrase to the configuration file
*/
int mem_only_psk;
/** /**
* pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
*/ */

View file

@ -1085,14 +1085,13 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected = NULL; struct wpa_bss *selected = NULL;
int prio; int prio;
struct wpa_ssid *next_ssid = NULL; struct wpa_ssid *next_ssid = NULL;
struct wpa_ssid *ssid;
if (wpa_s->last_scan_res == NULL || if (wpa_s->last_scan_res == NULL ||
wpa_s->last_scan_res_used == 0) wpa_s->last_scan_res_used == 0)
return NULL; /* no scan results from last update */ return NULL; /* no scan results from last update */
if (wpa_s->next_ssid) { if (wpa_s->next_ssid) {
struct wpa_ssid *ssid;
/* check that next_ssid is still valid */ /* check that next_ssid is still valid */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->next_ssid) if (ssid == wpa_s->next_ssid)
@ -1128,6 +1127,27 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
break; break;
} }
ssid = *selected_ssid;
if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
!ssid->passphrase && !ssid->ext_psk) {
const char *field_name, *txt = NULL;
wpa_dbg(wpa_s, MSG_DEBUG,
"PSK/passphrase not yet available for the selected network");
wpas_notify_network_request(wpa_s, ssid,
WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
field_name = wpa_supplicant_ctrl_req_to_string(
WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
if (field_name == NULL)
return NULL;
wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
selected = NULL;
}
return selected; return selected;
} }

View file

@ -1239,7 +1239,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
} }
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
int psk_set = 0;
if (ssid->psk_set) {
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
psk_set = 1;
}
#ifndef CONFIG_NO_PBKDF2 #ifndef CONFIG_NO_PBKDF2
if (bss && ssid->bssid_set && ssid->ssid_len == 0 && if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
ssid->passphrase) { ssid->passphrase) {
@ -1249,6 +1254,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
psk, PMK_LEN); psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk)); os_memset(psk, 0, sizeof(psk));
} }
#endif /* CONFIG_NO_PBKDF2 */ #endif /* CONFIG_NO_PBKDF2 */
@ -1286,6 +1292,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
"external passphrase)", "external passphrase)",
psk, PMK_LEN); psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk)); os_memset(psk, 0, sizeof(psk));
} else } else
#endif /* CONFIG_NO_PBKDF2 */ #endif /* CONFIG_NO_PBKDF2 */
@ -1298,6 +1305,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
return -1; return -1;
} }
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk)); os_memset(psk, 0, sizeof(psk));
} else { } else {
wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
@ -1311,6 +1319,12 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
ext_password_free(pw); ext_password_free(pw);
} }
#endif /* CONFIG_EXT_PASSWORD */ #endif /* CONFIG_EXT_PASSWORD */
if (!psk_set) {
wpa_msg(wpa_s, MSG_INFO,
"No PSK available for association");
return -1;
}
} else } else
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
@ -4967,6 +4981,15 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
str_clear_free(eap->external_sim_resp); str_clear_free(eap->external_sim_resp);
eap->external_sim_resp = os_strdup(value); eap->external_sim_resp = os_strdup(value);
break; break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
if (wpa_config_set(ssid, "psk", value, 0) < 0)
return -1;
ssid->mem_only_psk = 1;
if (ssid->passphrase)
wpa_config_update_psk(ssid);
if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
wpa_supplicant_req_scan(wpa_s, 0, 0);
break;
default: default:
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
return -1; return -1;
@ -5014,7 +5037,8 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
} }
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
(!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk) (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
!ssid->mem_only_psk)
return 1; return 1;
return 0; return 0;

View file

@ -740,6 +740,11 @@ fast_reauth=1
# startup and reconfiguration time can be optimized by generating the PSK only # startup and reconfiguration time can be optimized by generating the PSK only
# only when the passphrase or SSID has actually changed. # only when the passphrase or SSID has actually changed.
# #
# mem_only_psk: Whether to keep PSK/passphrase only in memory
# 0 = allow psk/passphrase to be stored to the configuration file
# 1 = do not store psk/passphrase to the configuration file
#mem_only_psk=0
#
# eapol_flags: IEEE 802.1X/EAPOL options (bit field) # eapol_flags: IEEE 802.1X/EAPOL options (bit field)
# Dynamic WEP key required for non-WPA mode # Dynamic WEP key required for non-WPA mode
# bit0 (1): require dynamically generated unicast WEP key # bit0 (1): require dynamically generated unicast WEP key

View file

@ -737,6 +737,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
return WPA_CTRL_REQ_EAP_PASSPHRASE; return WPA_CTRL_REQ_EAP_PASSPHRASE;
else if (os_strcmp(field, "SIM") == 0) else if (os_strcmp(field, "SIM") == 0)
return WPA_CTRL_REQ_SIM; return WPA_CTRL_REQ_SIM;
else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
return WPA_CTRL_REQ_PSK_PASSPHRASE;
return WPA_CTRL_REQ_UNKNOWN; return WPA_CTRL_REQ_UNKNOWN;
} }
@ -776,6 +778,10 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
case WPA_CTRL_REQ_SIM: case WPA_CTRL_REQ_SIM:
ret = "SIM"; ret = "SIM";
break; break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
*txt = "PSK or passphrase";
ret = "PSK_PASSPHRASE";
break;
default: default:
break; break;
} }
@ -789,6 +795,35 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
return ret; return ret;
} }
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt)
{
char *buf;
size_t buflen;
int len;
buflen = 100 + os_strlen(txt) + ssid->ssid_len;
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
field_name, ssid->id, txt);
if (os_snprintf_error(buflen, len)) {
os_free(buf);
return;
}
if (ssid->ssid && buflen > len + ssid->ssid_len) {
os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
len += ssid->ssid_len;
buf[len] = '\0';
}
buf[buflen - 1] = '\0';
wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
os_free(buf);
}
#ifdef IEEE8021X_EAPOL #ifdef IEEE8021X_EAPOL
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
static void wpa_supplicant_eap_param_needed(void *ctx, static void wpa_supplicant_eap_param_needed(void *ctx,
@ -798,9 +833,6 @@ static void wpa_supplicant_eap_param_needed(void *ctx,
struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_ssid *ssid = wpa_s->current_ssid;
const char *field_name, *txt = NULL; const char *field_name, *txt = NULL;
char *buf;
size_t buflen;
int len;
if (ssid == NULL) if (ssid == NULL)
return; return;
@ -817,25 +849,7 @@ static void wpa_supplicant_eap_param_needed(void *ctx,
wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name); wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
buflen = 100 + os_strlen(txt) + ssid->ssid_len; wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen,
WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
field_name, ssid->id, txt);
if (os_snprintf_error(buflen, len)) {
os_free(buf);
return;
}
if (ssid->ssid && buflen > len + ssid->ssid_len) {
os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
len += ssid->ssid_len;
buf[len] = '\0';
}
buf[buflen - 1] = '\0';
wpa_msg(wpa_s, MSG_INFO, "%s", buf);
os_free(buf);
} }
#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
#define wpa_supplicant_eap_param_needed NULL #define wpa_supplicant_eap_param_needed NULL

View file

@ -22,4 +22,7 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field); enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt);
#endif /* WPAS_GLUE_H */ #endif /* WPAS_GLUE_H */