/* 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 #include #include #include #include #include #include #include #include #include #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; }