master
c 161 lines 5.15 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "mcp_auth.h"
4 #include "claim/claim.h"
5 #include <fcntl.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <limits.h>
9
10 #ifdef NETDATA_MCP_DEV_PREVIEW_API_KEY
11
12 static char mcp_dev_preview_api_key[MCP_DEV_PREVIEW_API_KEY_LENGTH + 1] = "";
13
14 static bool mcp_api_key_generate_and_save(void) {
15 nd_uuid_t uuid;
16 uuid_generate_random(uuid);
17
18 // Unparse directly to the destination buffer
19 uuid_unparse_lower(uuid, mcp_dev_preview_api_key);
20
21 // Construct full path
22 char path[PATH_MAX];
23 snprintf(path, sizeof(path), "%s/%s", netdata_configured_varlib_dir, MCP_DEV_PREVIEW_API_KEY_FILENAME);
24
25 // Open file with O_CREAT | O_EXCL to ensure we don't overwrite
26 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
27 if (fd == -1) {
28 netdata_log_error("MCP: Failed to create API key file %s: %s",
29 path, strerror(errno));
30 return false;
31 }
32
33 // Write the UUID with newline
34 char buffer[MCP_DEV_PREVIEW_API_KEY_LENGTH + 2]; // +1 for newline, +1 for null
35 snprintf(buffer, sizeof(buffer), "%s\n", mcp_dev_preview_api_key);
36
37 ssize_t written = write(fd, buffer, MCP_DEV_PREVIEW_API_KEY_LENGTH + 1); // +1 for newline
38 if (written != (ssize_t)(MCP_DEV_PREVIEW_API_KEY_LENGTH + 1)) {
39 netdata_log_error("MCP: Failed to write API key to file: %s", strerror(errno));
40 close(fd);
41 unlink(path);
42 return false;
43 }
44
45 close(fd);
46
47 // Ensure file permissions are correct (only owner can read/write)
48 if (chmod(path, 0600) == -1) {
49 netdata_log_error("MCP: Failed to set permissions on API key file: %s", strerror(errno));
50 unlink(path);
51 return false;
52 }
53
54 netdata_log_info("MCP: Generated new developer preview API key");
55 return true;
56 }
57
58 static bool mcp_api_key_load(void) {
59 // Construct full path
60 char path[PATH_MAX];
61 snprintf(path, sizeof(path), "%s/%s", netdata_configured_varlib_dir, MCP_DEV_PREVIEW_API_KEY_FILENAME);
62
63 int fd = open(path, O_RDONLY);
64 if (fd == -1) {
65 if (errno == ENOENT) {
66 // File doesn't exist, this is expected on first run
67 return false;
68 }
69 netdata_log_error("MCP: Failed to open API key file %s: %s",
70 path, strerror(errno));
71 return false;
72 }
73
74 char buffer[MCP_DEV_PREVIEW_API_KEY_LENGTH + 2]; // +1 for potential newline, +1 for null
75 ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
76 close(fd);
77
78 if (bytes_read < MCP_DEV_PREVIEW_API_KEY_LENGTH || bytes_read > MCP_DEV_PREVIEW_API_KEY_LENGTH + 1) {
79 netdata_log_error("MCP: Invalid API key file size: expected %d or %d bytes, got %zd",
80 MCP_DEV_PREVIEW_API_KEY_LENGTH, MCP_DEV_PREVIEW_API_KEY_LENGTH + 1, bytes_read);
81 return false;
82 }
83
84 buffer[bytes_read] = '\0';
85
86 // Strip trailing newline if present
87 if (bytes_read > 0 && buffer[bytes_read - 1] == '\n') {
88 buffer[bytes_read - 1] = '\0';
89 }
90
91 // Basic validation - should be a valid UUID format
92 nd_uuid_t uuid;
93 if (uuid_parse(buffer, uuid) != 0) {
94 netdata_log_error("MCP: Invalid UUID format in API key file");
95 return false;
96 }
97
98 strncpy(mcp_dev_preview_api_key, buffer, MCP_DEV_PREVIEW_API_KEY_LENGTH);
99 mcp_dev_preview_api_key[MCP_DEV_PREVIEW_API_KEY_LENGTH] = '\0';
100
101 netdata_log_info("MCP: Loaded developer preview API key");
102 return true;
103 }
104
105 void mcp_api_key_initialize(void) {
106 // Try to load existing key first
107 if (!mcp_api_key_load()) {
108 // If loading fails, generate a new one
109 if (!mcp_api_key_generate_and_save()) {
110 netdata_log_error("MCP: Failed to initialize API key system");
111 return;
112 }
113 }
114
115 char path[PATH_MAX];
116 snprintf(path, sizeof(path), "%s/%s", netdata_configured_varlib_dir, MCP_DEV_PREVIEW_API_KEY_FILENAME);
117 #if defined(OS_WINDOWS)
118 char display_path[PATH_MAX];
119 netdata_log_info("MCP: Developer preview API key initialized. Location: %s",
120 os_translate_path(display_path, path, sizeof(display_path)));
121 #else
122 netdata_log_info("MCP: Developer preview API key initialized. Location: %s", path);
123 #endif
124 }
125
126 bool mcp_api_key_verify(const char *api_key, bool silent) {
127 if (!api_key || !*api_key) {
128 if (!silent)
129 netdata_log_error("MCP: No API key provided");
130 return false;
131 }
132
133 // Check if agent is claimed
134 if (!is_agent_claimed()) {
135 if (!silent)
136 netdata_log_error("MCP: API key authentication rejected - agent is not claimed to Netdata Cloud");
137 return false;
138 }
139
140 // Check if we have a loaded API key
141 if (!mcp_dev_preview_api_key[0]) {
142 if (!silent)
143 netdata_log_error("MCP: No API key loaded");
144 return false;
145 }
146
147 // Compare the keys
148 bool valid = (strcmp(api_key, mcp_dev_preview_api_key) == 0);
149
150 if (!valid && !silent) {
151 netdata_log_error("MCP: Invalid API key provided");
152 }
153
154 return valid;
155 }
156
157 const char *mcp_api_key_get(void) {
158 return mcp_dev_preview_api_key;
159 }
160
161 #endif // NETDATA_MCP_DEV_PREVIEW_API_KEY