2018-11-16 23:22:54 +01:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Wireless daemon for Linux
|
|
|
|
*
|
2019-10-25 00:43:08 +02:00
|
|
|
* Copyright (C) 2018-2019 Intel Corporation. All rights reserved.
|
2018-11-16 23:22:54 +01:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-04-03 18:33:36 +02:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2018-11-16 23:22:54 +01:00
|
|
|
#include <ell/ell.h>
|
|
|
|
|
2019-04-03 18:33:36 +02:00
|
|
|
#include "src/crypto.h"
|
|
|
|
#include "src/ie.h"
|
|
|
|
#include "src/handshake.h"
|
|
|
|
#include "src/owe.h"
|
|
|
|
#include "src/mpdu.h"
|
2019-05-03 20:59:53 +02:00
|
|
|
#include "src/auth-proto.h"
|
2018-11-16 23:22:54 +01:00
|
|
|
|
|
|
|
struct owe_sm {
|
|
|
|
struct handshake_state *hs;
|
2018-12-12 18:14:17 +01:00
|
|
|
const struct l_ecc_curve *curve;
|
|
|
|
struct l_ecc_scalar *private;
|
|
|
|
struct l_ecc_point *public_key;
|
2019-01-17 21:25:38 +01:00
|
|
|
uint8_t retry;
|
|
|
|
uint16_t group;
|
|
|
|
const unsigned int *ecc_groups;
|
2018-11-16 23:22:54 +01:00
|
|
|
};
|
|
|
|
|
2019-01-17 21:25:38 +01:00
|
|
|
static bool owe_reset(struct owe_sm *owe)
|
|
|
|
{
|
2024-02-27 19:33:58 +01:00
|
|
|
if (owe->hs->force_default_ecc_group) {
|
2022-08-08 20:22:57 +02:00
|
|
|
if (owe->retry != 0) {
|
|
|
|
l_warn("Forced default OWE group but was rejected!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_debug("Forcing default OWE group 19");
|
|
|
|
|
|
|
|
owe->retry++;
|
|
|
|
owe->group = 19;
|
|
|
|
|
|
|
|
goto get_curve;
|
|
|
|
}
|
|
|
|
|
2019-01-17 21:25:38 +01:00
|
|
|
/*
|
|
|
|
* Reset OWE with a different curve group and generate a new key pair
|
|
|
|
*/
|
|
|
|
if (owe->ecc_groups[owe->retry] == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
owe->group = owe->ecc_groups[owe->retry];
|
2022-08-08 20:22:57 +02:00
|
|
|
|
|
|
|
get_curve:
|
2021-07-08 03:56:53 +02:00
|
|
|
owe->curve = l_ecc_curve_from_ike_group(owe->group);
|
2019-01-17 21:25:38 +01:00
|
|
|
|
|
|
|
if (owe->private)
|
|
|
|
l_ecc_scalar_free(owe->private);
|
|
|
|
|
|
|
|
if (owe->public_key)
|
|
|
|
l_ecc_point_free(owe->public_key);
|
|
|
|
|
|
|
|
if (!l_ecdh_generate_key_pair(owe->curve, &owe->private,
|
|
|
|
&owe->public_key))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
void owe_sm_free(struct owe_sm *owe)
|
2018-11-16 23:22:54 +01:00
|
|
|
{
|
2018-12-12 18:14:17 +01:00
|
|
|
l_ecc_scalar_free(owe->private);
|
|
|
|
l_ecc_point_free(owe->public_key);
|
2018-11-16 23:22:54 +01:00
|
|
|
|
|
|
|
l_free(owe);
|
|
|
|
}
|
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
void owe_build_dh_ie(struct owe_sm *owe, uint8_t *buf, size_t *len_out)
|
2018-11-16 23:22:54 +01:00
|
|
|
{
|
|
|
|
/*
|
2021-09-03 21:35:44 +02:00
|
|
|
* A client wishing to do OWE ... MUST include a Diffie-Hellman
|
|
|
|
* Parameter element to its 802.11 association request.
|
2018-11-16 23:22:54 +01:00
|
|
|
*/
|
|
|
|
buf[0] = IE_TYPE_EXTENSION;
|
|
|
|
buf[2] = IE_TYPE_OWE_DH_PARAM - 256;
|
2019-01-17 21:25:38 +01:00
|
|
|
l_put_le16(owe->group, buf + 3); /* group */
|
2021-09-03 21:35:44 +02:00
|
|
|
*len_out = l_ecc_point_get_x(owe->public_key, buf + 5,
|
2018-12-12 18:14:17 +01:00
|
|
|
L_ECC_SCALAR_MAX_BYTES);
|
2021-09-03 21:35:44 +02:00
|
|
|
buf[1] = 3 + *len_out; /* length */
|
2018-11-16 23:22:54 +01:00
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
*len_out += 5;
|
2018-11-16 23:22:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RFC 8110 Section 4.4 Post Association
|
|
|
|
*/
|
|
|
|
static bool owe_compute_keys(struct owe_sm *owe, const void *public_key,
|
|
|
|
size_t pub_len)
|
|
|
|
{
|
2018-12-12 18:14:17 +01:00
|
|
|
struct l_ecc_scalar *shared_secret;
|
|
|
|
uint8_t ss_buf[L_ECC_SCALAR_MAX_BYTES];
|
2019-01-17 21:25:38 +01:00
|
|
|
uint8_t prk[L_ECC_SCALAR_MAX_BYTES];
|
|
|
|
uint8_t pmk[L_ECC_SCALAR_MAX_BYTES];
|
2018-11-16 23:22:54 +01:00
|
|
|
uint8_t pmkid[16];
|
2019-01-17 21:25:38 +01:00
|
|
|
uint8_t key[L_ECC_SCALAR_MAX_BYTES + L_ECC_SCALAR_MAX_BYTES + 2];
|
|
|
|
uint8_t *ptr = key;
|
2018-11-16 23:22:54 +01:00
|
|
|
struct iovec iov[2];
|
|
|
|
struct l_checksum *sha;
|
2018-12-12 18:14:17 +01:00
|
|
|
struct l_ecc_point *other_public;
|
2019-01-17 21:25:38 +01:00
|
|
|
ssize_t nbytes;
|
|
|
|
enum l_checksum_type type;
|
2018-11-16 23:22:54 +01:00
|
|
|
|
2018-12-12 18:14:17 +01:00
|
|
|
other_public = l_ecc_point_from_data(owe->curve,
|
|
|
|
L_ECC_POINT_TYPE_COMPLIANT,
|
|
|
|
public_key, pub_len);
|
|
|
|
if (!other_public) {
|
|
|
|
l_error("AP public key was not valid");
|
|
|
|
return false;
|
|
|
|
}
|
2018-11-19 20:48:23 +01:00
|
|
|
|
2018-12-28 15:38:05 +01:00
|
|
|
if (!l_ecdh_generate_shared_secret(owe->private, other_public,
|
|
|
|
&shared_secret)) {
|
2019-10-17 01:14:17 +02:00
|
|
|
l_ecc_point_free(other_public);
|
2018-11-16 23:22:54 +01:00
|
|
|
return false;
|
2018-12-12 18:14:17 +01:00
|
|
|
}
|
2018-11-16 23:22:54 +01:00
|
|
|
|
2018-12-12 18:14:17 +01:00
|
|
|
l_ecc_point_free(other_public);
|
2018-11-16 23:22:54 +01:00
|
|
|
|
2019-01-17 21:25:38 +01:00
|
|
|
nbytes = l_ecc_scalar_get_data(shared_secret, ss_buf, sizeof(ss_buf));
|
2018-12-12 18:14:17 +01:00
|
|
|
l_ecc_scalar_free(shared_secret);
|
|
|
|
|
2019-10-17 00:29:50 +02:00
|
|
|
if (nbytes < 0)
|
|
|
|
return false;
|
|
|
|
|
2019-01-17 21:25:38 +01:00
|
|
|
ptr += l_ecc_point_get_x(owe->public_key, ptr, sizeof(key));
|
|
|
|
memcpy(ptr, public_key, nbytes);
|
|
|
|
ptr += nbytes;
|
|
|
|
l_put_le16(owe->group, ptr);
|
|
|
|
ptr += 2;
|
|
|
|
|
|
|
|
switch (owe->group) {
|
|
|
|
case 19:
|
|
|
|
type = L_CHECKSUM_SHA256;
|
|
|
|
break;
|
|
|
|
case 20:
|
|
|
|
type = L_CHECKSUM_SHA384;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto failed;
|
|
|
|
}
|
2018-11-19 20:48:23 +01:00
|
|
|
|
2018-11-16 23:22:54 +01:00
|
|
|
/* prk = HKDF-extract(C | A | group, z) */
|
2019-01-17 21:25:38 +01:00
|
|
|
if (!hkdf_extract(type, key, ptr - key, 1, prk, ss_buf, nbytes))
|
2018-11-16 23:22:54 +01:00
|
|
|
goto failed;
|
|
|
|
|
|
|
|
/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
|
2021-06-29 21:51:05 +02:00
|
|
|
if (!hkdf_expand(type, prk, nbytes, "OWE Key Generation", pmk, nbytes))
|
2018-11-16 23:22:54 +01:00
|
|
|
goto failed;
|
|
|
|
|
2019-01-17 21:25:38 +01:00
|
|
|
sha = l_checksum_new(type);
|
2018-11-16 23:22:54 +01:00
|
|
|
|
|
|
|
/* PMKID = Truncate-128(Hash(C | A)) */
|
2019-01-17 21:25:38 +01:00
|
|
|
iov[0].iov_base = key; /* first nbytes of key are owe->public_key */
|
|
|
|
iov[0].iov_len = nbytes;
|
2018-11-16 23:22:54 +01:00
|
|
|
iov[1].iov_base = (void *) public_key;
|
2019-01-17 21:25:38 +01:00
|
|
|
iov[1].iov_len = nbytes;
|
2018-11-16 23:22:54 +01:00
|
|
|
|
|
|
|
l_checksum_updatev(sha, iov, 2);
|
|
|
|
|
|
|
|
l_checksum_get_digest(sha, pmkid, 16);
|
|
|
|
|
|
|
|
l_checksum_free(sha);
|
|
|
|
|
2019-01-17 21:25:38 +01:00
|
|
|
handshake_state_set_pmk(owe->hs, pmk, nbytes);
|
2018-11-16 23:22:54 +01:00
|
|
|
handshake_state_set_pmkid(owe->hs, pmkid);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
failed:
|
2018-12-12 18:14:17 +01:00
|
|
|
memset(ss_buf, 0, sizeof(ss_buf));
|
2018-11-16 23:22:54 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
bool owe_next_group(struct owe_sm *owe)
|
2019-05-03 20:59:53 +02:00
|
|
|
{
|
|
|
|
/* retry with another group, if possible */
|
|
|
|
owe->retry++;
|
|
|
|
|
|
|
|
if (!owe_reset(owe))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
int owe_process_dh_ie(struct owe_sm *owe, const uint8_t *dh, size_t len)
|
2018-11-16 23:22:54 +01:00
|
|
|
{
|
2021-09-03 21:35:44 +02:00
|
|
|
if (!dh || len < 34) {
|
2018-11-16 23:22:54 +01:00
|
|
|
l_error("associate response did not include proper OWE IE's");
|
2019-05-03 20:59:53 +02:00
|
|
|
goto invalid_ies;
|
2018-11-16 23:22:54 +01:00
|
|
|
}
|
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
if (l_get_le16(dh) != owe->group) {
|
2018-11-16 23:22:54 +01:00
|
|
|
l_error("associate response contained unsupported group %u",
|
2021-09-03 21:35:44 +02:00
|
|
|
l_get_le16(dh));
|
2019-05-03 20:59:53 +02:00
|
|
|
return -EBADMSG;
|
2018-11-16 23:22:54 +01:00
|
|
|
}
|
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
if (!owe_compute_keys(owe, dh + 2, len - 2)) {
|
2018-11-16 23:22:54 +01:00
|
|
|
l_error("could not compute OWE keys");
|
2019-05-03 20:59:53 +02:00
|
|
|
return -EBADMSG;
|
2018-11-16 23:22:54 +01:00
|
|
|
}
|
|
|
|
|
2019-05-03 20:59:53 +02:00
|
|
|
return 0;
|
2018-11-16 23:22:54 +01:00
|
|
|
|
2019-05-03 20:59:53 +02:00
|
|
|
invalid_ies:
|
|
|
|
return MMPDU_STATUS_CODE_INVALID_ELEMENT;
|
2018-11-16 23:22:54 +01:00
|
|
|
}
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
struct owe_sm *owe_sm_new(struct handshake_state *hs)
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
{
|
2019-05-03 20:59:53 +02:00
|
|
|
struct owe_sm *owe = l_new(struct owe_sm, 1);
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
|
2019-05-03 20:59:53 +02:00
|
|
|
owe->hs = hs;
|
2021-07-08 03:56:53 +02:00
|
|
|
owe->ecc_groups = l_ecc_supported_ike_groups();
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
|
2019-05-03 20:59:53 +02:00
|
|
|
if (!owe_reset(owe)) {
|
|
|
|
l_free(owe);
|
|
|
|
return NULL;
|
|
|
|
}
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
|
2021-09-03 21:35:44 +02:00
|
|
|
return owe;
|
netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association
processing was done in netdev_connect_event. This led to
netdev_connect_event having to handle all the logic of both success and
failure, as well as parsing the association for FT and OWE. Also, without
checking the status code in the associate frame there is the potential
for the kernel to think we are connected even if association failed
(e.g. rogue AP).
This change introduces two flags into netdev, expect_connect_failure and
ignore_connect_event. All the FT processing that was once in
netdev_connect_event has now been moved into netdev_associate_event, as
well as non-FT associate frame processing. The connect event now only
handles failure cases for soft/half MAC cards.
Note: Since fullmac cards rely on the connect event, the eapol_start
and netdev_connect_ok were left in netdev_connect_event. Since neither
auth/assoc events come in on fullmac we shouldn't have any conflict with
the new flags.
Once a connection has completed association, EAPoL is started from
netdev_associate_event (if required) and the ignore_connect_event flag can
be set. This will bypass the connect event.
If a connection has failed during association for whatever reason, we can
set expect_connect_failure, the netdev reason, and the MPDU status code.
This allows netdev_connect_event to both handle the error, and, if required,
send a deauth telling the kernel that we have failed (protecting against the
rogue AP situation).
2019-03-05 22:42:33 +01:00
|
|
|
}
|