Files
wifimngr/wps.c
2025-01-20 14:34:04 +01:00

365 lines
9.2 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* wps.c - provides "wifi.wps" ubus object
*
* Copyright (C) 2018-2024 Iopsys Software Solutions AB. All rights reserved.
* Copyright (C) 2025 Genexis AB.
*
*/
#include <stdio.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <uci.h>
#include <easy/easy.h>
#include <wifi.h>
#include "wifimngr.h"
#include "policy.c"
char *wifi_get_main_interface(struct wifimngr *w, int freq_band, char *ifmain)
{
enum wifi_bw bw;
int i;
for (i = 0; i < w->num_wifi_device; i++) {
uint32_t ch = 0;
if (!wifi_get_channel(w->wdev[i].phy, &ch, &bw)) {
if (freq_band == 5 && (ch >= 36 && ch < 200)) {
strncpy(ifmain, w->wdev[i].phy, 15);
return ifmain;
} else if (freq_band == 2 && (ch > 0 && ch <= 14)) {
strncpy(ifmain, w->wdev[i].phy, 15);
return ifmain;
}
}
}
return NULL;
}
static int wps_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifimngr *w = container_of(obj, struct wifimngr, wifi_wps_obj);
struct blob_attr *tb[__ATTR_IFNAME_ONLY];
enum wps_status code = 0;
char ifname[16] = {0};
struct blob_buf bb;
char status[16];
memset(&bb, 0, sizeof(bb));
blobmsg_parse(ifname_policy, __ATTR_IFNAME_ONLY, tb, blob_data(msg),
blob_len(msg));
if (!(tb[WPS_ATTR_IFNAME])) {
if (wifi_get_main_interface(w, 5, ifname) == NULL)
return UBUS_STATUS_UNKNOWN_ERROR;
} else {
strncpy(ifname, blobmsg_data(tb[WPS_ATTR_IFNAME]), 15);
}
if (wifi_get_wps_status(ifname, &code))
return UBUS_STATUS_UNKNOWN_ERROR;
switch (code) {
case WPS_STATUS_INIT:
strcpy(status, "init");
break;
case WPS_STATUS_PROCESSING:
strcpy(status, "processing");
break;
case WPS_STATUS_SUCCESS:
strcpy(status, "success");
break;
case WPS_STATUS_FAIL:
strcpy(status, "fail");
break;
case WPS_STATUS_TIMEOUT:
strcpy(status, "timeout");
break;
default:
strcpy(status, "unknown");
break;
}
blob_buf_init(&bb, 0);
blobmsg_add_u32(&bb, "code", code);
blobmsg_add_string(&bb, "status", status);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wps_start(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifimngr *w = container_of(obj, struct wifimngr, wifi_wps_obj);
struct blob_attr *tb[__WPS_START_ATTR_MAX];
struct wps_param wps = {
.role = WPS_REGISTRAR,
.method = WPS_METHOD_PBC
};
char ifname[16] = {0};
char role[16] = {0};
char mode[8] = {0};
unsigned long pin;
blobmsg_parse(wps_start_policy, __WPS_START_ATTR_MAX,
tb, blob_data(msg), blob_len(msg));
if (!(tb[WPS_START_ATTR_IFNAME])) {
/* if interface not provided, assume 5Ghz main interface */
if (wifi_get_main_interface(w, 5, ifname) == NULL)
return UBUS_STATUS_UNKNOWN_ERROR;
} else {
strncpy(ifname, blobmsg_data(tb[WPS_START_ATTR_IFNAME]), 15);
}
if (!(tb[WPS_START_ATTR_MODE])) {
wps.method = WPS_METHOD_PBC;
} else {
strncpy(mode, blobmsg_data(tb[WPS_START_ATTR_MODE]), 7);
if (!strcasecmp(mode, "pin"))
wps.method = WPS_METHOD_PIN;
else if (!strcasecmp(mode, "pbc"))
wps.method = WPS_METHOD_PBC;
else
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (!(tb[WPS_START_ATTR_ROLE])) {
wps.role = WPS_REGISTRAR;
} else {
strncpy(role, blobmsg_data(tb[WPS_START_ATTR_ROLE]), 10);
if (!strcasecmp(role, "registrar"))
wps.role = WPS_REGISTRAR;
else if (!strcasecmp(role, "enrollee"))
wps.role = WPS_ENROLLEE;
else if (!strcasecmp(role, "bsta"))
wps.role = WPS_ENROLLEE_BSTA;
else
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (wps.method == WPS_METHOD_PIN) {
if (!(tb[WPS_START_ATTR_STA_PIN])) {
return UBUS_STATUS_INVALID_ARGUMENT;
} else {
pin = strtoul(blobmsg_data(tb[WPS_START_ATTR_STA_PIN]), NULL, 10);
if (!wifi_is_wps_pin_valid(pin))
return UBUS_STATUS_INVALID_ARGUMENT;
wps.pin = pin;
}
}
if ((wifi_start_wps(ifname, wps) == 0))
return UBUS_STATUS_OK;
return UBUS_STATUS_UNKNOWN_ERROR;
}
static int wps_genpin(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
unsigned long PIN;
char local_devPwd[32];
struct blob_buf bb;
memset(&bb, 0, sizeof(bb));
wifi_generate_wps_pin(&PIN);
sprintf(local_devPwd, "%08u", (unsigned int)PIN);
local_devPwd[8] = '\0';
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "pin", local_devPwd);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
static int wps_checkpin(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
unsigned long pin;
bool valid = false;
struct blob_attr *tb[__ATTR_PIN_MAX];
struct blob_buf bb;
memset(&bb, 0, sizeof(bb));
blobmsg_parse(pin_policy, __ATTR_PIN_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[ATTR_PIN]))
return UBUS_STATUS_INVALID_ARGUMENT;
pin = strtoul(blobmsg_get_string(tb[ATTR_PIN]), NULL, 10);
valid = wifi_is_wps_pin_valid(pin);
blob_buf_init(&bb, 0);
blobmsg_add_u8(&bb, "valid", valid);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wps_set_ap_pin(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifimngr *w = container_of(obj, struct wifimngr, wifi_wps_obj);
struct blob_attr *tb[__PIN_SET_MAX];
char ifname[16] = {0};
unsigned long pin;
blobmsg_parse(pin_set_policy, __PIN_SET_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[PIN_SET_IFNAME])) {
if (wifi_get_main_interface(w, 5, ifname) == NULL)
return UBUS_STATUS_UNKNOWN_ERROR;
} else {
strncpy(ifname, blobmsg_data(tb[PIN_SET_IFNAME]), 15);
}
if (!(tb[PIN_SET_PIN]))
return UBUS_STATUS_INVALID_ARGUMENT;
pin = strtoul(blobmsg_data(tb[PIN_SET_PIN]), NULL, 10);
if (!wifi_is_wps_pin_valid(pin))
return UBUS_STATUS_INVALID_ARGUMENT;
if (wifi_set_wps_pin(ifname, pin))
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
static int wps_show_ap_pin(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifimngr *w = container_of(obj, struct wifimngr, wifi_wps_obj);
struct blob_attr *tb[__ATTR_IFNAME_ONLY];
char ifname[16] = {0};
char pinstr[9] = {0};
struct blob_buf bb;
unsigned long pin;
memset(&bb, 0, sizeof(bb));
blobmsg_parse(ifname_policy, __ATTR_IFNAME_ONLY, tb, blob_data(msg),
blob_len(msg));
if (!(tb[WPS_ATTR_IFNAME])) {
if (wifi_get_main_interface(w, 5, ifname) == NULL)
return UBUS_STATUS_UNKNOWN_ERROR;
} else {
strncpy(ifname, blobmsg_data(tb[WPS_ATTR_IFNAME]), 15);
}
if (wifi_get_wps_pin(ifname, &pin))
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
sprintf(pinstr, "%08lu", pin);
blobmsg_add_string(&bb, "pin", pinstr);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wps_stop(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifimngr *w = container_of(obj, struct wifimngr, wifi_wps_obj);
struct blob_attr *tb[__ATTR_IFNAME_ONLY];
char ifname[16] = {0};
int i;
blobmsg_parse(ifname_policy, __ATTR_IFNAME_ONLY, tb, blob_data(msg),
blob_len(msg));
if (tb[WPS_ATTR_IFNAME]) {
strncpy(ifname, blobmsg_data(tb[WPS_ATTR_IFNAME]), 15);
wifi_stop_wps(ifname);
} else {
for (i = 0; i < w->num_wifi_iface; i++) {
wifi_stop_wps(w->ifs[i].iface);
}
}
return 0;
}
int wl_wps_help(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return wl_help_command(ctx, obj, req, method, msg, "wifi.wps");
}
#define MAX_WPS_METHODS 10
int add_wps_methods(struct wifimngr *w, struct ubus_object *wifi_wps_obj)
{
struct ubus_method *wps_methods;
int n_methods = 0;
wps_methods = calloc(MAX_WPS_METHODS, sizeof(struct ubus_method));
if (!wps_methods)
return -ENOMEM;
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD_NOARG("help", wl_wps_help));
/* Usage -
* start {"mode":"pbc|pin", "role":"enrollee|registrar", "pin":"pin"}
* where default mode = pbc and default role = registrar.
* If role = registrar, and mode = pin, then enrollee's pin
* should be provided through the 'pin' attribute.
*/
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD("start", wps_start, wps_start_policy));
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD_NOARG("stop", wps_stop));
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD("status", wps_status, ifname_policy));
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD_NOARG("generate_pin", wps_genpin));
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD("validate_pin", wps_checkpin, pin_policy));
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD("showpin", wps_show_ap_pin, ifname_policy));
UBUS_METHOD_ADD(wps_methods, n_methods,
UBUS_METHOD("setpin", wps_set_ap_pin, pin_set_policy));
wifi_wps_obj->methods = wps_methods;
wifi_wps_obj->n_methods = n_methods;
return 0;
}