# Implementation Plan: Enhanced Admin Event API Structure ## Current Issue The current admin event routing at [`main.c:3248-3268`](src/main.c:3248) has a security vulnerability: ```c if (event_kind == 23455 || event_kind == 23456) { // Admin event processing int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi); } else { // Regular event storage and broadcasting } ``` **Problem**: Any event with these kinds gets routed to admin processing, regardless of authorization. This allows unauthorized users to send admin events that could be processed as legitimate admin commands. **Note**: Event kinds 33334 and 33335 are no longer used and have been removed from the admin event routing. ## Required Security Enhancement Admin events must be validated for proper authorization BEFORE routing to admin processing: 1. **Relay Public Key Check**: Event must have a `p` tag equal to the relay's public key 2. **Admin Signature Check**: Event must be signed by an authorized admin private key 3. **Fallback to Regular Processing**: If authorization fails, treat as regular event (not admin event) ## Implementation Plan ### Phase 1: Add Admin Authorization Validation #### 1.1 Create Consolidated Admin Authorization Function **Location**: [`src/main.c`](src/main.c) or [`src/config.c`](src/config.c) ```c /** * Consolidated admin event authorization validator * Implements defense-in-depth security for admin events * * @param event - The event to validate for admin authorization * @param error_message - Buffer for detailed error messages * @param error_size - Size of error message buffer * @return 0 if authorized, -1 if unauthorized, -2 if validation error */ int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size) { if (!event) { snprintf(error_message, error_size, "admin_auth: null event"); return -2; } // Extract event components cJSON* kind_obj = cJSON_GetObjectItem(event, "kind"); cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey"); cJSON* tags_obj = cJSON_GetObjectItem(event, "tags"); if (!kind_obj || !pubkey_obj || !tags_obj) { snprintf(error_message, error_size, "admin_auth: missing required fields"); return -2; } // Validation Layer 1: Kind Check int event_kind = (int)cJSON_GetNumberValue(kind_obj); if (event_kind != 23455 && event_kind != 23456) { snprintf(error_message, error_size, "admin_auth: not an admin event kind"); return -1; } // Validation Layer 2: Relay Targeting Check const char* relay_pubkey = get_config_value("relay_pubkey"); if (!relay_pubkey) { snprintf(error_message, error_size, "admin_auth: relay pubkey not configured"); return -2; } // Check for 'p' tag targeting this relay int has_relay_target = 0; if (cJSON_IsArray(tags_obj)) { cJSON* tag = NULL; cJSON_ArrayForEach(tag, tags_obj) { if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) { cJSON* tag_name = cJSON_GetArrayItem(tag, 0); cJSON* tag_value = cJSON_GetArrayItem(tag, 1); if (cJSON_IsString(tag_name) && cJSON_IsString(tag_value)) { const char* name = cJSON_GetStringValue(tag_name); const char* value = cJSON_GetStringValue(tag_value); if (strcmp(name, "p") == 0 && strcmp(value, relay_pubkey) == 0) { has_relay_target = 1; break; } } } } } if (!has_relay_target) { // Admin event for different relay - not unauthorized, just not for us snprintf(error_message, error_size, "admin_auth: admin event for different relay"); return -1; } // Validation Layer 3: Admin Signature Check (only if targeting this relay) const char* event_pubkey = cJSON_GetStringValue(pubkey_obj); if (!event_pubkey) { snprintf(error_message, error_size, "admin_auth: invalid pubkey format"); return -2; } const char* admin_pubkey = get_config_value("admin_pubkey"); if (!admin_pubkey || strcmp(event_pubkey, admin_pubkey) != 0) { // This is the ONLY case where we log as "Unauthorized admin event attempt" // because it's targeting THIS relay but from wrong admin snprintf(error_message, error_size, "admin_auth: unauthorized admin for this relay"); log_warning("SECURITY: Unauthorized admin event attempt for this relay"); return -1; } // All validation layers passed log_info("ADMIN: Admin event authorized"); return 0; } ``` #### 1.2 Update Event Routing Logic **Location**: [`main.c:3248`](src/main.c:3248) ```c // Current problematic code: if (event_kind == 23455 || event_kind == 23456) { // Admin event processing int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi); } else { // Regular event storage and broadcasting } // Enhanced secure code with consolidated authorization: if (result == 0) { cJSON* kind_obj = cJSON_GetObjectItem(event, "kind"); if (kind_obj && cJSON_IsNumber(kind_obj)) { int event_kind = (int)cJSON_GetNumberValue(kind_obj); // Check if this is an admin event if (event_kind == 23455 || event_kind == 23456) { // Use consolidated authorization check char auth_error[512] = {0}; int auth_result = is_authorized_admin_event(event, auth_error, sizeof(auth_error)); if (auth_result == 0) { // Authorized admin event - process through admin API char admin_error[512] = {0}; int admin_result = process_admin_event_in_config(event, admin_error, sizeof(admin_error), wsi); if (admin_result != 0) { result = -1; strncpy(error_message, admin_error, sizeof(error_message) - 1); } // Admin events are NOT broadcast to subscriptions } else { // Unauthorized admin event - treat as regular event log_warning("Unauthorized admin event treated as regular event"); if (store_event(event) != 0) { result = -1; strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1); } else { broadcast_event_to_subscriptions(event); } } } else { // Regular event - normal processing if (store_event(event) != 0) { result = -1; strncpy(error_message, "error: failed to store event", sizeof(error_message) - 1); } else { broadcast_event_to_subscriptions(event); } } } } ``` ### Phase 2: Enhanced Admin Event Processing #### 2.1 Admin Event Validation in Config System **Location**: [`src/config.c`](src/config.c) - [`process_admin_event_in_config()`](src/config.c:2065) Add additional validation within the admin processing function: ```c int process_admin_event_in_config(cJSON* event, char* error_buffer, size_t error_buffer_size, struct lws* wsi) { // Double-check authorization (defense in depth) if (!is_authorized_admin_event(event)) { snprintf(error_buffer, error_buffer_size, "unauthorized: not a valid admin event"); return -1; } // Continue with existing admin event processing... // ... rest of function unchanged } ``` #### 2.2 Logging and Monitoring Add comprehensive logging for admin event attempts: ```c // In the routing logic - enhanced logging cJSON* kind_obj = cJSON_GetObjectItem(event, "kind"); cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey"); int event_kind = kind_obj ? cJSON_GetNumberValue(kind_obj) : -1; const char* event_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : "unknown"; if (is_authorized_admin_event(event)) { char log_msg[256]; snprintf(log_msg, sizeof(log_msg), "ADMIN EVENT: Authorized admin event (kind=%d) from pubkey=%.16s...", event_kind, event_pubkey); log_info(log_msg); } else if (event_kind == 23455 || event_kind == 23456) { // This catches unauthorized admin event attempts char log_msg[256]; snprintf(log_msg, sizeof(log_msg), "SECURITY: Unauthorized admin event attempt (kind=%d) from pubkey=%.16s...", event_kind, event_pubkey); log_warning(log_msg); } ``` ## Phase 3: Unified Output Flow Architecture ### 3.1 Current Output Flow Analysis After analyzing both [`main.c`](src/main.c) and [`config.c`](src/config.c), the **admin event responses already flow through the standard WebSocket output pipeline**. This is the correct architecture and requires no changes. #### Standard WebSocket Output Pipeline **Regular Events** ([`main.c:2978-2996`](src/main.c:2978)): ```c // Database query responses unsigned char* buf = malloc(LWS_PRE + msg_len); memcpy(buf + LWS_PRE, msg_str, msg_len); lws_write(wsi, buf + LWS_PRE, msg_len, LWS_WRITE_TEXT); free(buf); ``` **OK Responses** ([`main.c:3342-3375`](src/main.c:3342)): ```c // Event processing results: ["OK", event_id, success_boolean, message] unsigned char *buf = malloc(LWS_PRE + response_len); memcpy(buf + LWS_PRE, response_str, response_len); lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT); free(buf); ``` #### Admin Event Output Pipeline (Already Unified) **Admin Responses** ([`config.c:2363-2414`](src/config.c:2363)): ```c // Admin query responses use IDENTICAL pattern int send_websocket_response_data(struct lws* wsi, cJSON* response_data) { unsigned char* buf = malloc(LWS_PRE + response_len); memcpy(buf + LWS_PRE, response_str, response_len); // Same lws_write() call as regular events int result = lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT); free(buf); return result; } ``` ### 3.2 Unified Output Flow Confirmation ✅ **Admin responses already use the same WebSocket transmission mechanism as regular events** ✅ **Both admin and regular events use identical buffer allocation patterns** ✅ **Both admin and regular events use the same [`lws_write()`](src/config.c:2393) function** ✅ **Both admin and regular events follow the same cleanup patterns** ### 3.3 Output Flow Integration Points The admin event processing in [`config.c:2436`](src/config.c:2436) already integrates correctly with the unified output system: 1. **Admin Query Processing** ([`config.c:2568-2583`](src/config.c:2568)): - Auth queries return structured JSON via [`send_websocket_response_data()`](src/config.c:2571) - System commands return status data via [`send_websocket_response_data()`](src/config.c:2631) 2. **Response Format Consistency**: - Admin responses use standard JSON format - Regular events use standard Nostr event format - Both transmitted through same WebSocket pipeline 3. **Error Handling Consistency**: - Admin errors returned via same WebSocket connection - Regular event errors returned via OK messages - Both use identical transmission mechanism ### 3.4 Key Architectural Benefits **No Changes Required**: The output flow is already unified and correctly implemented. **Security Separation**: Admin events are processed separately but responses flow through the same secure WebSocket channel. **Performance Consistency**: Both admin and regular responses use the same optimized transmission path. **Maintenance Simplicity**: Single WebSocket output pipeline reduces complexity and potential bugs. ### 3.5 Admin Event Flow Summary ``` Admin Event Input → Authorization Check → Admin Processing → Unified WebSocket Output Regular Event Input → Validation → Storage + Broadcast → Unified WebSocket Output ``` Both flows converge at the **Unified WebSocket Output** stage, which is already correctly implemented. ## Phase 4: Integration Points for Secure Admin Event Routing ### 4.1 Configuration System Integration **Required Configuration Values**: - `admin_pubkey` - Public key of authorized administrator - `relay_pubkey` - Public key of this relay instance **Integration Points**: 1. [`get_config_value()`](src/config.c) - Used by authorization function 2. [`get_relay_pubkey_cached()`](src/config.c) - Used for relay targeting validation 3. Configuration loading during startup - Must ensure admin/relay pubkeys are available ### 4.3 Forward Declarations Required **Location**: [`src/main.c`](src/main.c) - Add near other forward declarations (around line 230) ```c // Forward declarations for enhanced admin event authorization int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size); ``` ### 4.4 Error Handling Integration **Enhanced Error Response System**: ```c // In main.c event processing - enhanced error handling for admin events if (auth_result != 0) { // Admin authorization failed - send detailed OK response cJSON* event_id = cJSON_GetObjectItem(event, "id"); if (event_id && cJSON_IsString(event_id)) { cJSON* response = cJSON_CreateArray(); cJSON_AddItemToArray(response, cJSON_CreateString("OK")); cJSON_AddItemToArray(response, cJSON_CreateString(cJSON_GetStringValue(event_id))); cJSON_AddItemToArray(response, cJSON_CreateBool(0)); // Failed cJSON_AddItemToArray(response, cJSON_CreateString(auth_error)); // Send via standard WebSocket output pipeline char *response_str = cJSON_Print(response); if (response_str) { size_t response_len = strlen(response_str); unsigned char *buf = malloc(LWS_PRE + response_len); if (buf) { memcpy(buf + LWS_PRE, response_str, response_len); lws_write(wsi, buf + LWS_PRE, response_len, LWS_WRITE_TEXT); free(buf); } free(response_str); } cJSON_Delete(response); } } ``` ### 4.5 Logging Integration Points **Console Logging**: Uses existing [`log_warning()`](src/main.c:993), [`log_info()`](src/main.c:972) functions **Security Event Categories**: - Admin authorization success logged via `log_info()` - Admin authorization failures logged via `log_warning()` - Admin event processing logged via existing admin logging ## Phase 5: Detailed Function Specifications ### 5.1 Core Authorization Function **Function**: `is_authorized_admin_event()` **Location**: [`src/main.c`](src/main.c) or [`src/config.c`](src/config.c) **Dependencies**: - `get_config_value()` for admin/relay pubkeys - `log_warning()` and `log_info()` for logging - `cJSON` library for event parsing **Return Values**: - `0` - Event is authorized for admin processing - `-1` - Event is unauthorized (treat as regular event) - `-2` - Validation error (malformed event) **Error Handling**: Detailed error messages in provided buffer for client feedback ### 5.2 Enhanced Event Routing **Location**: [`main.c:3248-3340`](src/main.c:3248) **Integration**: Replaces existing admin event routing logic **Dependencies**: - `is_authorized_admin_event()` for authorization - `process_admin_event_in_config()` for admin processing - `store_event()` and `broadcast_event_to_subscriptions()` for regular events **Security Features**: - Graceful degradation for unauthorized admin events - Comprehensive logging of authorization attempts - No broadcast of admin events to subscriptions - Detailed error responses for failed authorization ### 5.4 Defense-in-Depth Validation **Primary Validation**: In main event routing logic **Secondary Validation**: In `process_admin_event_in_config()` function **Tertiary Validation**: In individual admin command handlers **Validation Layers**: 1. **Kind Check** - Must be admin event kind (23455/23456) 2. **Relay Targeting Check** - Must have 'p' tag with this relay's pubkey 3. **Admin Signature Check** - Must be signed by authorized admin (only if targeting this relay) 4. **Processing Check** - Additional validation in admin handlers **Security Logic**: - If no 'p' tag for this relay → Admin event for different relay (not unauthorized) - If 'p' tag for this relay + wrong admin signature → "Unauthorized admin event attempt" ## Phase 6: Event Flow Documentation ### 6.1 Complete Event Processing Flow ``` ┌─────────────────┐ │ WebSocket Input │ └─────────┬───────┘ │ ▼ ┌─────────────────┐ │ Unified │ │ Validation │ ← nostr_validate_unified_request() └─────────┬───────┘ │ ▼ ┌─────────────────┐ │ Kind-Based │ │ Routing Check │ ← Check if kind 23455/23456 └─────────┬───────┘ │ ┌────▼────┐ │ Admin? │ └────┬────┘ │ ┌─────▼─────┐ ┌─────────────┐ │ YES │ │ NO │ │ │ │ │ ▼ │ ▼ │ ┌─────────────┐ │ ┌─────────────┐ │ │ Admin │ │ │ Regular │ │ │ Authorization│ │ │ Event │ │ │ Check │ │ │ Processing │ │ └─────┬───────┘ │ └─────┬───────┘ │ │ │ │ │ ┌────▼────┐ │ ▼ │ │Authorized?│ │ ┌─────────────┐ │ └────┬────┘ │ │ store_event()│ │ │ │ │ + │ │ ┌─────▼─────┐ │ │ broadcast() │ │ │ YES NO │ │ └─────┬───────┘ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ ▼ │ │┌─────┐┌───┴┐ │ ┌─────────────┐ │ ││Admin││Treat│ │ │ WebSocket │ │ ││API ││as │ │ │ OK Response │ │ ││ ││Reg │ │ └─────────────┘ │ │└──┬──┘└───┬┘ │ │ │ │ │ │ │ │ ▼ │ │ │ │┌─────────┐│ │ │ ││WebSocket││ │ │ ││Response ││ │ │ │└─────────┘│ │ │ └───────────┴───┘ │ │ │ └───────────────────────────┘ │ ▼ ┌─────────────┐ │ Unified │ │ WebSocket │ │ Output │ └─────────────┘ ``` ### 6.2 Security Decision Points 1. **Event Kind Check** - Identifies potential admin events 2. **Authorization Validation** - Three-layer security check 3. **Routing Decision** - Admin API vs Regular processing 4. **Response Generation** - Unified output pipeline 5. **Audit Logging** - Security event tracking ### 6.3 Error Handling Paths **Validation Errors**: Return detailed error messages via OK response **Authorization Failures**: Log security event + treat as regular event **Processing Errors**: Return admin-specific error responses **System Errors**: Fallback to standard error handling This completes the comprehensive implementation plan for the enhanced admin event API structure with unified output flow architecture.