refactor #1 + adding documentation
Validate source documentation / generated-wiki (pull_request) Failing after 10s

This commit is contained in:
2026-07-19 20:25:12 +00:00
parent db81260d7f
commit b37e0d6bce
31 changed files with 4068 additions and 2598 deletions
+361
View File
@@ -0,0 +1,361 @@
/*
* Handle controller commands, adoption state changes, provisioning results,
* firmware-version spoofing, and protocol-safe response diagnostics.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <json-c/json.h>
#include "inform.h"
#include "crypto.h"
#include "http.h"
#include "wlan.h"
#include "state.h"
#include "config.h"
#include "sysinfo.h"
#include "clients.h"
#include "lldp.h"
#include "inform_internal.h"
static int inform_valid_auth_key(const char *key)
{
if (!key || strlen(key) != 32)
return 0;
for (size_t i = 0; i < 32; i++)
if (!isxdigit((unsigned char)key[i]))
return 0;
return 1;
}
static int protocol_debug_level;
void inform_set_debug_level(int level)
{
protocol_debug_level = level < 0 ? 0 : level > 2 ? 2 : level;
}
int inform_debug_level(void)
{
return protocol_debug_level;
}
static int inform_debug_system_cfg_key(const char *key)
{
if (!key || (strncmp(key, "aaa.", 4) &&
strncmp(key, "wireless.", 9)))
return 0;
return strstr(key, ".ssid") || strstr(key, ".id") ||
strstr(key, ".vap_ind") || strstr(key, ".parent");
}
void inform_log_controller_response(struct json_object *response,
const char *raw_json)
{
if (protocol_debug_level <= 0 || !response)
return;
LOG("Protocol debug: decrypted controller response fields follow");
json_object_object_foreach(response, key, value) {
LOG("Protocol field: %s type=%s", key,
json_type_to_name(json_object_get_type(value)));
}
struct json_object *system_cfg_object;
if (json_object_object_get_ex(response, "system_cfg",
&system_cfg_object)) {
const char *system_cfg = json_object_get_string(system_cfg_object);
char *copy = system_cfg ? strdup(system_cfg) : NULL;
if (copy) {
char *save = NULL;
for (char *line = strtok_r(copy, "\n", &save);
line; line = strtok_r(NULL, "\n", &save)) {
line[strcspn(line, "\r")] = '\0';
char *equals = strchr(line, '=');
if (!equals)
continue;
*equals = '\0';
if (inform_debug_system_cfg_key(line))
LOG("Protocol system_cfg: %s=%s", line, equals + 1);
}
free(copy);
}
}
if (protocol_debug_level >= 2) {
LOG("WARNING: full decrypted response may contain credentials");
LOG("Protocol response JSON: %s", raw_json ? raw_json : "");
}
}
/* ═══════════════════════════════════════════════════════════════════
sys_stats — CPU and memory of the system
═══════════════════════════════════════════════════════════════════
The controller shows CPU and RAM in the device view.
We read /proc/stat and /proc/meminfo directly.
*/
static void reboot_openwrt(void)
{
LOG("Controller requested an OpenWrt reboot");
int status = system("/sbin/reboot");
if (status != 0)
LOG("OpenWrt reboot command failed with status=%d", status);
}
/* ═══════════════════════════════════════════════════════════════════
Process JSON command from the controller
═══════════════════════════════════════════════════════════════════
_type == "noop" → do nothing
_type == "reboot" → reboot OpenWrt
_type == "cmd" → set-adopt / legacy reboot / reset / locate
_type == "setstate" → apply radio_table + vap_table via UCI
_type == "setparam" → change a single parameter
*/
void inform_handle_response(openuf_state_t *st,
const uf_model_t *model,
struct json_object *resp,
char *action_out)
{
struct json_object *v;
const char *type = "noop";
if (json_object_object_get_ex(resp, "_type", &v) &&
json_object_is_type(v, json_type_string))
type = json_object_get_string(v);
LOG("Handling response type: %s", type);
/* An OpenWrt host cannot install UniFi firmware. Acknowledge the
* controller's request by reporting its target version from now on. */
if (!strcmp(type, "upgrade")) {
if (json_object_object_get_ex(resp, "version", &v) &&
json_object_is_type(v, json_type_string)) {
const char *version = json_object_get_string(v);
size_t len = strlen(version);
if (len > 0 && len < sizeof(st->firmware_version)) {
snprintf(st->firmware_version,
sizeof(st->firmware_version), "%s", version);
state_save(st);
LOG("Firmware upgrade spoofed; now reporting version=%s",
st->firmware_version);
strcpy(action_out, "upgrade-spoofed");
return;
}
}
LOG("Ignoring upgrade response without a valid version");
strcpy(action_out, "upgrade-invalid");
return;
}
/* ── noop ────────────────────────────────────────────────────── */
if (!strcmp(type, "noop")) {
strcpy(action_out, "noop");
return;
}
/* Modern controllers send reboot as a top-level response type rather
* than wrapping it in {"_type":"cmd","cmd":"reboot"}. */
if (!strcmp(type, "reboot")) {
strcpy(action_out, "reboot");
reboot_openwrt();
return;
}
/* ── setparam ────────────────────────────────────────────────── */
if (!strcmp(type, "setparam")) {
int received_adoption_key = 0;
int applied_system_cfg = 0;
/* First parse mgmt_cfg used by modern controllers. */
if (json_object_object_get_ex(resp, "mgmt_cfg", &v)) {
const char *mgmt_cfg = json_object_get_string(v);
LOG("Parsing mgmt_cfg: %s", mgmt_cfg);
/* Parse newline-separated key=value pairs. */
char cfg_copy[2048];
strncpy(cfg_copy, mgmt_cfg, sizeof(cfg_copy)-1);
cfg_copy[sizeof(cfg_copy)-1] = '\0';
char *line = strtok(cfg_copy, "\n");
while (line) {
char *eq = strchr(line, '=');
if (eq) {
*eq = '\0';
const char *key = line;
const char *val = eq + 1;
if (!strcmp(key, "authkey"))
LOG("mgmt_cfg param: authkey = %.8s...", val);
else
LOG("mgmt_cfg param: %s = %s", key, val);
if (!strcmp(key, "authkey")) {
if (inform_valid_auth_key(val) &&
strcmp(st->authkey, val) != 0) {
int replacing_key = st->authkey[0] &&
strcmp(st->authkey, DEFAULT_AUTH_KEY) != 0;
strncpy(st->authkey, val,
sizeof(st->authkey)-1);
st->authkey[sizeof(st->authkey)-1] = '\0';
received_adoption_key = 1;
LOG("%s device key from setparam",
replacing_key ? "Replaced" : "Accepted");
} else if (!inform_valid_auth_key(val)) {
LOG("Ignoring invalid authkey from setparam");
}
} else if (!strcmp(key, "cfgversion")) {
/*
* This is the version the controller wants, not proof
* that its setstate has been applied locally.
*/
LOG("Controller requested cfgversion=%s; currently applied=%s",
val, st->cfgversion);
} else if (!strcmp(key, "use_aes_gcm")) {
st->use_aes_gcm = !strcmp(val, "true") ||
!strcmp(val, "1");
LOG("AES-GCM %s for subsequent inform packets",
st->use_aes_gcm ? "enabled" : "disabled");
} else if (!strcmp(key, "mgmt_url")) {
/* Could save mgmt_url for future use */
}
/* Other management parameters are currently informational. */
}
line = strtok(NULL, "\n");
}
}
struct json_object *system_cfg_obj;
if (json_object_object_get_ex(resp, "system_cfg",
&system_cfg_obj)) {
const char *system_cfg = json_object_get_string(system_cfg_obj);
LOG("Applying legacy system_cfg, length=%zu",
strlen(system_cfg));
if (wlan_apply_system_cfg(system_cfg, model) == 0) {
applied_system_cfg = 1;
st->config_applied = true;
st->config_schema = OPENUF_CONFIG_SCHEMA;
if (json_object_object_get_ex(resp, "cfgversion", &v))
snprintf(st->cfgversion, sizeof(st->cfgversion), "%s",
json_object_get_string(v));
LOG("Legacy system_cfg applied successfully, cfgversion=%s",
st->cfgversion);
} else {
st->config_applied = false;
strncpy(st->cfgversion, "0",
sizeof(st->cfgversion) - 1);
LOG("Legacy system_cfg failed; requesting provisioning retry");
}
}
/* Fall back to the direct key/value format used by older controllers. */
if (json_object_object_get_ex(resp, "key", &v)) {
const char *key = json_object_get_string(v);
struct json_object *val_o;
if (json_object_object_get_ex(resp, "value", &val_o)) {
const char *val = json_object_get_string(val_o);
LOG("setparam key=%s val=%s", key, val);
if (!strcmp(key, "inform_url"))
strncpy(st->inform_url, val, sizeof(st->inform_url)-1);
else if (!strcmp(key, "authkey") && inform_valid_auth_key(val) &&
strcmp(st->authkey, val) != 0) {
int replacing_key = st->authkey[0] &&
strcmp(st->authkey, DEFAULT_AUTH_KEY) != 0;
strncpy(st->authkey, val, sizeof(st->authkey)-1);
st->authkey[sizeof(st->authkey)-1] = '\0';
received_adoption_key = 1;
LOG("%s device key from direct setparam",
replacing_key ? "Replaced" : "Accepted");
}
}
}
/*
* Modern controllers complete adoption by returning the per-device
* key in setparam. Mark the device adopted before its next inform so
* both the payload and packet encryption switch to that key.
*/
if (received_adoption_key) {
st->adopted = true;
LOG("Adoption completed through setparam; next inform will use the controller key");
}
state_save(st);
LOG("State saved after setparam");
strcpy(action_out, applied_system_cfg ? "provisioned" :
received_adoption_key ? "adopted" : "setparam");
return;
}
/* ── cmd ─────────────────────────────────────────────────────── */
if (!strcmp(type, "cmd")) {
const char *cmd = "";
if (json_object_object_get_ex(resp, "cmd", &v))
cmd = json_object_get_string(v);
if (!strcmp(cmd, "set-adopt") || !strcmp(cmd, "adopt")) {
if (json_object_object_get_ex(resp, "uri", &v))
strncpy(st->inform_url, json_object_get_string(v),
sizeof(st->inform_url)-1);
if (json_object_object_get_ex(resp, "key", &v))
strncpy(st->authkey, json_object_get_string(v),
sizeof(st->authkey)-1);
st->adopted = true;
state_save(st);
strcpy(action_out, "adopted");
LOG("Adopted successfully. Key: %.8s...", st->authkey);
} else if (!strcmp(cmd, "reboot")) {
strcpy(action_out, "reboot");
reboot_openwrt();
} else if (!strcmp(cmd, "reset")) {
strcpy(action_out, "reset");
system("rm -f " OPENUF_STATE_FILE);
reboot_openwrt();
} else if (!strcmp(cmd, "locate")) {
/* Blink LED — on OpenWrt: echo 1 > /sys/class/leds/.../trigger */
strcpy(action_out, "locate");
} else {
snprintf(action_out, 64, "cmd:%s", cmd);
}
return;
}
/* ── setstate — WiFi configuration from the controller ──────────── */
if (!strcmp(type, "setstate")) {
if (json_object_object_get_ex(resp, "cfgversion", &v))
snprintf(st->cfgversion, sizeof(st->cfgversion),
"%s", json_object_get_string(v));
struct json_object *rt = NULL, *vt = NULL;
json_object_object_get_ex(resp, "radio_table", &rt);
json_object_object_get_ex(resp, "vap_table", &vt);
int apply_ok = 0;
if (rt || vt) {
printf("[openuf] Applying controller WiFi configuration...\n");
apply_ok = wlan_apply_config(resp, model) == 0;
} else {
LOG("setstate contained neither radio_table nor vap_table");
}
st->config_applied = apply_ok;
if (apply_ok)
st->config_schema = OPENUF_CONFIG_SCHEMA;
if (!apply_ok) {
strncpy(st->cfgversion, "0", sizeof(st->cfgversion) - 1);
LOG("WiFi configuration failed; cfgversion reset so the controller retries");
}
state_save(st);
strcpy(action_out, apply_ok ? "setstate" : "setstate-failed");
return;
}
snprintf(action_out, 64, "unknown:%s", type);
}