Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 96 additions & 4 deletions ClientKit/Api.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Api.h"
#include "mach/mach_port.h"
#include "pthread.h"
#include <stdio.h>

static pthread_mutex_t* api_mutex = NULL;

Expand All @@ -27,10 +28,16 @@ bool get_platform_info(platform_info_t *info) {
goto error;
}

strcpy(info->device_info_str, driver_info.bsd_name);
strcpy(info->driver_info_str, driver_info.driver_version);
strcat(info->driver_info_str, " ");
strcat(info->driver_info_str, driver_info.fw_version);
// driver_version + " " + fw_version routinely exceeds the 32-byte
// driver_info_str; strcat here smashed the caller's stack. The kernel's
// strings also aren't guaranteed NUL-terminated, hence the %.*s bounds.
snprintf(info->device_info_str, sizeof(info->device_info_str), "%.*s",
(int)strnlen(driver_info.bsd_name, sizeof(driver_info.bsd_name)), driver_info.bsd_name);
snprintf(info->driver_info_str, sizeof(info->driver_info_str), "%.*s %.*s",
(int)strnlen(driver_info.driver_version, sizeof(driver_info.driver_version)),
driver_info.driver_version,
(int)strnlen(driver_info.fw_version, sizeof(driver_info.fw_version)),
driver_info.fw_version);
return true;

error:
Expand Down Expand Up @@ -297,6 +304,19 @@ kern_return_t associate_ssid(const char *ssid, const char *pwd)
return ioctl_set(IOCTL_80211_ASSOCIATE, &ass, sizeof(struct ioctl_associate));
}

kern_return_t associate_ssid_enterprise(const char *ssid)
{
struct ioctl_associate_enterprise req;
memset(&req, 0, sizeof(req));
req.version = IOCTL_VERSION;

size_t ssid_len = strnlen(ssid, NWID_LEN);
memcpy(req.nwid.nwid, ssid, ssid_len);
req.nwid.len = (unsigned int)ssid_len;

return ioctl_set(IOCTL_80211_ASSOCIATE_ENTERPRISE, &req, sizeof(req));
}

kern_return_t dis_associate_ssid(const char *ssid)
{
struct ioctl_disassociate dis;
Expand All @@ -305,6 +325,78 @@ kern_return_t dis_associate_ssid(const char *ssid)
return ioctl_set(IOCTL_80211_DISASSOCIATE, &dis, sizeof(struct ioctl_disassociate));
}

kern_return_t set_eap_pmk(const char *ssid, enum itl80211_eap_status status,
const unsigned char *pmk, unsigned int pmk_len)
{
struct ioctl_eap_pmk req;
memset(&req, 0, sizeof(req));
req.version = IOCTL_VERSION;

// strnlen, not strlen: never trust ssid to be a properly NUL-terminated
// string shorter than NWID_LEN just because the caller says so.
size_t ssid_len = strnlen(ssid, NWID_LEN);
memcpy(req.ssid, ssid, ssid_len);
req.ssid_len = (unsigned int)ssid_len;
req.status = status;

if (status == ITL_EAP_STATUS_SUCCESS) {
if (pmk == NULL || pmk_len != PMK_LEN) {
return KERN_INVALID_ARGUMENT;
}
memcpy(req.pmk, pmk, PMK_LEN);
req.pmk_len = PMK_LEN;
}

kern_return_t kr = ioctl_set(IOCTL_80211_WPA_KEY, &req, sizeof(req));

// Don't let a derived PMK linger in this stack frame any longer than
// the syscall that consumes it needs it for. memset_s, unlike memset,
// can't be optimized away as a dead store.
memset_s(&req, sizeof(req), 0, sizeof(req));

return kr;
}

kern_return_t send_eapol_frame(const unsigned char *frame, unsigned int len)
{
if (frame == NULL || len < 18 || len > EAPOL_MAX_FRAME) {
return KERN_INVALID_ARGUMENT;
}
struct ioctl_eapol_tx *req = calloc(1, sizeof(*req));
if (req == NULL) {
return KERN_RESOURCE_SHORTAGE;
}
req->version = IOCTL_VERSION;
req->len = len;
memcpy(req->frame, frame, len);
kern_return_t kr = ioctl_set(IOCTL_80211_TX_EAPOL, req, sizeof(*req));
free(req);
return kr;
}

kern_return_t receive_eapol_frame(unsigned char *frame, unsigned int capacity, unsigned int *len)
{
if (frame == NULL || len == NULL) {
return KERN_INVALID_ARGUMENT;
}
*len = 0;
struct ioctl_eapol_rx *rx = calloc(1, sizeof(*rx));
if (rx == NULL) {
return KERN_RESOURCE_SHORTAGE;
}
kern_return_t kr = ioctl_get(IOCTL_80211_RX_EAPOL, rx, sizeof(*rx));
if (kr == KERN_SUCCESS && rx->len > 0) {
if (rx->len > capacity || rx->len > EAPOL_MAX_FRAME) {
kr = KERN_INVALID_ARGUMENT;
} else {
memcpy(frame, rx->frame, rx->len);
*len = rx->len;
}
}
free(rx);
return kr;
}

void api_terminate(void) {
if (api_mutex) {
/* acquire API lock to wait for the pending API call */
Expand Down
20 changes: 20 additions & 0 deletions ClientKit/Api.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,28 @@ kern_return_t join_ssid(const char *ssid, const char *pwd);

kern_return_t associate_ssid(const char *ssid, const char *pwd);

// WPA2/WPA-Enterprise: must be called before starting the userspace EAP
// handshake — locks the driver's ic_des_essid onto `ssid` so a PMK
// delivered later via set_eap_pmk() isn't rejected for an SSID mismatch.
kern_return_t associate_ssid_enterprise(const char *ssid);

kern_return_t dis_associate_ssid(const char *ssid);

// Delivers a PEAP/MSCHAPv2-derived PMK (or an EAP failure status) for `ssid`
// down to itlwm via IOCTL_80211_WPA_KEY. `pmk`/`pmk_len` are only consulted
// when status == ITL_EAP_STATUS_SUCCESS; pass NULL/0 for any other status.
kern_return_t set_eap_pmk(const char *ssid, enum itl80211_eap_status status,
const unsigned char *pmk, unsigned int pmk_len);

// Transmits a complete Ethernet II EAPOL frame (EtherType 0x888E) through
// itlwm's send queue (IOCTL_80211_TX_EAPOL).
kern_return_t send_eapol_frame(const unsigned char *frame, unsigned int len);

// Fetches the next EAPOL frame itlwm queued for userspace
// (IOCTL_80211_RX_EAPOL). *len is 0 when none is waiting. Fails on itlwm
// builds without 802.1X support, so it doubles as a capability probe.
kern_return_t receive_eapol_frame(unsigned char *frame, unsigned int capacity, unsigned int *len);

void api_terminate(void);

#endif /* Api_h */
53 changes: 53 additions & 0 deletions ClientKit/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,32 @@ struct ioctl_wpa_key {
char key[WPA_KEY_LEN];
};

#define PMK_LEN 32

enum itl80211_eap_status {
ITL_EAP_STATUS_IDLE = 0,
ITL_EAP_STATUS_SUCCESS = 1, /* pmk[] is valid */
ITL_EAP_STATUS_FAILED = 2, /* authenticator rejected the credentials */
ITL_EAP_STATUS_TIMEOUT = 3,
ITL_EAP_STATUS_ERROR = 4
};

/*
* IOCTL_80211_WPA_KEY (set): result of a userspace 802.1X/EAP exchange for
* `ssid`. On success the PMK is added to the PMKSA cache for the 4-way
* handshake; any other status makes the driver leave the network.
*/
#pragma pack(push, 1)
struct ioctl_eap_pmk {
unsigned int version;
unsigned char ssid[NWID_LEN];
unsigned int ssid_len;
enum itl80211_eap_status status;
unsigned int pmk_len;
unsigned char pmk[PMK_LEN];
};
#pragma pack(pop)

struct ioctl_associate {
unsigned int version;
struct ioctl_nw_id nwid;
Expand All @@ -106,6 +132,33 @@ struct ioctl_disassociate {
unsigned char ssid[NWID_LEN];
};

/*
* IOCTL_80211_ASSOCIATE_ENTERPRISE (set): join `nwid` with RSN, AKM 802.1X and
* no PSK. Send before starting EAP; WPA_KEY only accepts a result for this SSID.
*/
struct ioctl_associate_enterprise {
unsigned int version;
struct ioctl_nw_id nwid;
};

/*
* IOCTL_80211_TX_EAPOL (set) / IOCTL_80211_RX_EAPOL (get): complete Ethernet II
* EAPOL frames (EtherType 0x888E) between the driver and a userspace
* supplicant. RX returns len == 0 when no frame is queued.
*/
#define EAPOL_MAX_FRAME 1600
struct ioctl_eapol_tx {
unsigned int version;
unsigned int len;
unsigned char frame[EAPOL_MAX_FRAME];
};

struct ioctl_eapol_rx {
unsigned int version;
unsigned int len;
unsigned char frame[EAPOL_MAX_FRAME];
};

struct ioctl_join {
unsigned int version;
struct ioctl_nw_id nwid;
Expand Down
36 changes: 22 additions & 14 deletions ClientKit/IoctlId.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
//
// IoctlId.h
// HeliPort
//
// Created by 钟先耀 on 2020/4/8.
// Copyright © 2020 OpenIntelWireless. All rights reserved.
//

/*
* This program and the accompanying materials are licensed and made available
* under the terms and conditions of the The 3-Clause BSD License
* which accompanies this distribution. The full text of the license may be found at
* https://opensource.org/licenses/BSD-3-Clause
*/
* Copyright (C) 2020 钟先耀
*
* This program 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.
*
* This program 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.
*/

#ifndef IoctlId_h
#define IoctlId_h
Expand All @@ -30,7 +29,16 @@ enum IOCTL_IDS {
IOCTL_80211_SCAN_RESULT,
IOCTL_80211_TX_POWER_LEVEL,
IOCTL_80211_NW_BSSID,

/*
* Never call: itlwm builds before these additions mis-handle a selector
* equal to their IOCTL_ID_MAX (13) and panic. Newer selectors are safely
* rejected by them, so clients can probe with IOCTL_80211_RX_EAPOL.
*/
IOCTL_80211_RESERVED,
IOCTL_80211_ASSOCIATE_ENTERPRISE,
IOCTL_80211_TX_EAPOL,
IOCTL_80211_RX_EAPOL,

IOCTL_ID_MAX
};

Expand Down
Loading