Repository navigation
Expand file tree
/
Copy pathnpc.php
More file actions
176 lines (149 loc) · 7.28 KB
/
Copy pathnpc.php
File metadata and controls
176 lines (149 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<?php
/*
+-------------------------------------------------------------------------+
| Nagios Plugin for Cacti |
| |
| Copyright (C) 2007 Billy Gunn (billy@gunn.org) |
| Copyright (C) 2004-2026 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
+-------------------------------------------------------------------------+
| Cacti and Nagios are the copyright of their respective owners. |
+-------------------------------------------------------------------------+
*/
chdir('../../');
require_once('include/auth.php');
/* Allowlisted modules and their permitted actions. Prevents path traversal
via the module parameter and arbitrary method invocation via action. */
$allowed_modules = array(
'layout' => array('drawFrame'),
'hosts' => array('getHosts', 'getStateInfo', 'summary', 'getPerfData', 'getMappedGraph', 'setMappedGraph'),
'services' => array('getServices', 'getStateInfo', 'summary', 'getPerfData', 'getPerfHistory', 'getMappedGraph', 'setMappedGraph'),
'hostgroups' => array('getHostgroupHostStatus', 'getHostgroupServiceStatus', 'getOverview', 'getHosts', 'getHostList', 'getHostgroups'),
'servicegroups' => array('getHostStatusPortlet', 'getServicegroupServiceStatus', 'getOverview', 'getHostSummary', 'getServices', 'getServicegroups'),
'comments' => array('getAck', 'getLastComment', 'getComments', 'getHostComments', 'getServiceComments', 'deleteAllHostComments', 'deleteAllServiceComments', 'getHostIcon'),
'downtime' => array('getDowntime', 'getHostDowntime', 'getServiceDowntime', 'inDowntime', 'getTriggeredByCombo', 'scheduledDowntime', 'downtimeHistory'),
'notifications' => array('getNotifications'),
'logentries' => array('getLogs'),
'statehistory' => array('getStateHistory'),
'nagios' => array('getProgramStatus', 'getProcessInfoGrid', 'processInfo', 'command', 'checkPerf'),
'settings' => array('save'),
'cacti' => array('getHostTemplates', 'getHostnames', 'addDataInputMethod', 'generateHash', 'getGraphList'),
'sync' => array('import', 'getHosts', 'listHostgroups'),
);
$module = 'layout';
$action = 'drawFrame';
if (isset_request_var('module')) {
$module = get_filter_request_var('module', FILTER_CALLBACK, array('options' => 'sanitize_search_string'));
}
if (isset_request_var('action')) {
$action = get_filter_request_var('action', FILTER_CALLBACK, array('options' => 'sanitize_search_string'));
}
/* CSRF token refresh endpoint */
if ($action == 'csrf') {
print csrf_get_tokens();
exit;
}
/* Validate module against allowlist (prevents path traversal) */
if (!isset($allowed_modules[$module])) {
cacti_log('SECURITY: NPC rejected unknown module: ' . $module, false, 'NPC');
raise_message('npc_error', __('Invalid module requested.', 'npc'), MESSAGE_LEVEL_ERROR);
header('Location: npc.php');
exit;
}
/* Validate action against allowlist (prevents arbitrary method invocation) */
if (!in_array($action, $allowed_modules[$module], true)) {
cacti_log('SECURITY: NPC rejected unknown action: ' . $module . '/' . $action, false, 'NPC');
raise_message('npc_error', __('Invalid action requested.', 'npc'), MESSAGE_LEVEL_ERROR);
header('Location: npc.php');
exit;
}
/* Actions that change state are only accepted from POST, the one method
whose request csrf-magic validates. */
$post_only = array(
'nagios' => array('command'),
'comments' => array('deleteAllHostComments', 'deleteAllServiceComments'),
'cacti' => array('addDataInputMethod'),
'sync' => array('import'),
'hosts' => array('setMappedGraph'),
'services' => array('setMappedGraph'),
'settings' => array('save'),
);
if (isset($post_only[$module]) && in_array($action, $post_only[$module], true)
&& (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST')) {
cacti_log('SECURITY: NPC rejected non-POST request for ' . $module . '/' . $action, false, 'NPC');
http_response_code(405);
header('Allow: POST');
print json_encode(array('success' => false, 'msg' => __('This action requires a POST request.', 'npc')));
exit;
}
/* Include the validated controller */
require_once(dirname(__FILE__) . '/controllers/controller.php');
$controller_path = dirname(__FILE__) . '/controllers/' . $module . '.php';
if (!file_exists($controller_path)) {
cacti_log('ERROR: NPC controller not found: ' . $module, false, 'NPC');
raise_message('npc_error', __('Controller not found.', 'npc'), MESSAGE_LEVEL_ERROR);
header('Location: npc.php');
exit;
}
require_once($controller_path);
$class = 'Npc' . ucfirst($module) . 'Controller';
if (!class_exists($class)) {
cacti_log('ERROR: NPC controller class not found: ' . $class, false, 'NPC');
header('Location: npc.php');
exit;
}
$obj = new $class;
/* Collect sanitized parameters. Only allow known parameter prefixes. Only the
names in $request_properties are copied onto the controller, so a request
cannot overwrite its other properties. */
$request_properties = array('state', 'id', 'searchString', 'searchFields', 'hostgroup', 'unhandled');
$params = array('config' => $config);
if (is_array($_REQUEST)) {
foreach ($_REQUEST as $key => $value) {
/* Only accept p_ prefixed parameters, start, limit, and sort params */
if (preg_match('/^p_[a-zA-Z0-9_]+$/', $key)) {
$parm = preg_replace('/^p_/', '', $key);
/* $params['config'] carries the Cacti configuration */
if ($parm === 'config') {
continue;
}
if ($parm === 'searchFields' || $parm === 'data') {
/* JSON documents. The search sanitizer would strip their quotes; the
controllers only decode them and match field names against fixed
maps. */
$parsed = get_nfilter_request_var($key);
$parsed = is_string($parsed) ? $parsed : '';
} else {
$parsed = get_filter_request_var($key, FILTER_CALLBACK, array('options' => 'sanitize_search_string'));
}
if (in_array($parm, $request_properties, true)) {
$obj->$parm = $parsed;
}
$params[$parm] = $parsed;
} elseif (in_array($key, array('start', 'limit', 'sort', 'dir'), true)) {
if ($key == 'start') {
$obj->start = max(0, (int) get_filter_request_var($key, FILTER_VALIDATE_INT));
} elseif ($key == 'limit') {
$obj->limit = min(1000, max(1, (int) get_filter_request_var($key, FILTER_VALIDATE_INT)));
} elseif ($key == 'dir') {
$val = strtoupper(get_filter_request_var($key, FILTER_CALLBACK, array('options' => 'sanitize_search_string')));
$obj->dir = in_array($val, array('ASC', 'DESC'), true) ? $val : 'ASC';
} elseif ($key == 'sort') {
$obj->sort = get_filter_request_var($key, FILTER_CALLBACK, array('options' => 'sanitize_search_string'));
}
$params[$key] = $obj->$key;
}
}
}
/* Set the current page for pagination */
if (isset($obj->start) && isset($obj->limit) && $obj->limit > 0) {
$obj->currentPage = intval($obj->start / $obj->limit) + 1;
}
$out = $obj->$action($params);
if ($out) {
print $out;
}