source: nscp/modules/NRPEClient/NRPEClient.cpp @ c0d7e82

0.4.00.4.10.4.2
Last change on this file since c0d7e82 was c0d7e82, checked in by Michael Medin <michael@…>, 3 years ago
  • Property mode set to 100644
File size: 19.5 KB
Line 
1/**************************************************************************
2*   Copyright (C) 2004-2007 by Michael Medin <michael@medin.name>         *
3*                                                                         *
4*   This code is part of NSClient++ - http://trac.nakednuns.org/nscp      *
5*                                                                         *
6*   This program is free software; you can redistribute it and/or modify  *
7*   it under the terms of the GNU General Public License as published by  *
8*   the Free Software Foundation; either version 2 of the License, or     *
9*   (at your option) any later version.                                   *
10*                                                                         *
11*   This program is distributed in the hope that it will be useful,       *
12*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14*   GNU General Public License for more details.                          *
15*                                                                         *
16*   You should have received a copy of the GNU General Public License     *
17*   along with this program; if not, write to the                         *
18*   Free Software Foundation, Inc.,                                       *
19*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20***************************************************************************/
21#include "stdafx.h"
22#include "NRPEClient.h"
23#include <strEx.h>
24#include <time.h>
25#include <config.h>
26#include <msvc_wrappers.h>
27//#include <execute_process.hpp>
28#include <strEx.h>
29#include <boost/filesystem.hpp>
30
31
32NRPEClient gNRPEClient;
33
34#ifdef _WIN32
35BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
36{
37        NSCModuleWrapper::wrapDllMain(hModule, ul_reason_for_call);
38        return TRUE;
39}
40#endif
41
42NRPEClient::NRPEClient() : buffer_length_(0) {
43}
44
45NRPEClient::~NRPEClient() {
46}
47
48bool NRPEClient::loadModule(NSCAPI::moduleLoadMode mode) {
49        std::list<std::wstring> commands;
50        buffer_length_ = SETTINGS_GET_INT(nrpe::PAYLOAD_LENGTH);
51        try {
52                SETTINGS_REG_PATH(nrpe::CH_SECTION);
53                commands = NSCModuleHelper::getSettingsSection(setting_keys::nrpe::CH_SECTION_PATH);
54        } catch (NSCModuleHelper::NSCMHExcpetion &e) {
55                NSC_LOG_ERROR_STD(_T("Failed to register command: ") + e.msg_);
56        } catch (...) {
57                NSC_LOG_ERROR_STD(_T("Failed to register command."));
58        }
59
60        boost::filesystem::wpath p = NSCModuleHelper::getBasePath() + std::wstring(_T("security/nrpe_dh_512.pem"));
61        cert_ = p.string();
62        if (boost::filesystem::is_regular_file(p)) {
63                NSC_DEBUG_MSG_STD(_T("Using certificate: ") + cert_);
64        } else {
65                NSC_LOG_ERROR_STD(_T("Certificate not found: ") + cert_);
66        }
67
68
69        for (std::list<std::wstring>::const_iterator it = commands.begin(); it != commands.end(); ++it) {
70                NSC_DEBUG_MSG_STD(*it);
71                std::wstring s = NSCModuleHelper::getSettingsString(setting_keys::nrpe::CH_SECTION_PATH, (*it), _T(""));
72                if (s.empty()) {
73                        NSC_LOG_ERROR_STD(_T("Invalid NRPE-client entry: ") + (*it));
74                } else {
75                        addCommand((*it).c_str(), s);
76                }
77        }
78        return true;
79}
80
81void NRPEClient::add_options(po::options_description &desc, nrpe_connection_data &command_data) {
82        desc.add_options()
83                ("help,h", "Show this help message.")
84                ("host,H", po::wvalue<std::wstring>(&command_data.host), "The address of the host running the NRPE daemon")
85                ("port,p", po::value<int>(&command_data.port), "The port on which the daemon is running (default=5666)")
86                ("command,c", po::wvalue<std::wstring>(&command_data.command), "The name of the command that the remote daemon should run")
87                ("timeout,t", po::value<int>(&command_data.timeout), "Number of seconds before connection times out (default=10)")
88                ("buffer-length,l", po::value<unsigned int>(&command_data.buffer_length), std::string("Length of payload (has to be same as on the server (default=" + to_string(buffer_length_) + ")").c_str())
89                ("no-ssl,n", po::value<bool>(&command_data.no_ssl)->zero_tokens()->default_value(false), "Do not initial an ssl handshake with the server, talk in plaintext.")
90                ("arguments,a", po::wvalue<std::vector<std::wstring> >(&command_data.argument_vector), "list of arguments")
91                ;
92}
93
94
95void NRPEClient::addCommand(strEx::blindstr key, std::wstring args) {
96        try {
97
98                NRPEClient::nrpe_connection_data command_data;
99                boost::program_options::variables_map vm;
100
101                po::options_description desc("Allowed options");
102                buffer_length_ = SETTINGS_GET_INT(nrpe::PAYLOAD_LENGTH);
103                add_options(desc, command_data);
104
105                po::positional_options_description p;
106                p.add("arguments", -1);
107
108                std::vector<std::wstring> list;
109                //explicit escaped_list_separator(Char e = '\\', Char c = ',',Char q = '\"')
110                boost::escaped_list_separator<wchar_t> sep(L'\\', L' ', L'\"');
111                typedef boost::tokenizer<boost::escaped_list_separator<wchar_t>,std::wstring::const_iterator, std::wstring > tokenizer_t;
112                tokenizer_t tok(args, sep);
113                for(tokenizer_t::iterator beg=tok.begin(); beg!=tok.end();++beg){
114                        list.push_back(*beg);
115                }
116
117                po::wparsed_options parsed = po::basic_command_line_parser<wchar_t>(list).options(desc).positional(p).run();
118                po::store(parsed, vm);
119                po::notify(vm);
120                command_data.parse_arguments();
121
122                NSC_DEBUG_MSG_STD(_T("Added NRPE Client: ") + key.c_str() + _T(" = ") + command_data.toString());
123                commands[key] = command_data;
124
125                NSCModuleHelper::registerCommand(key.c_str(), command_data.toString());
126
127        } catch (boost::program_options::validation_error &e) {
128                NSC_LOG_ERROR_STD(_T("Could not parse: ") + key.c_str() + strEx::string_to_wstring(e.what()));
129        } catch (...) {
130                NSC_LOG_ERROR_STD(_T("Could not parse: ") + key.c_str());
131        }
132}
133
134bool NRPEClient::unloadModule() {
135        return true;
136}
137
138bool NRPEClient::hasCommandHandler() {
139        return true;
140}
141bool NRPEClient::hasMessageHandler() {
142        return false;
143}
144NSCAPI::nagiosReturn NRPEClient::handleCommand(const std::wstring command, std::list<std::wstring> arguments, std::wstring &message, std::wstring &perf)
145{
146        command_list::const_iterator cit = commands.find(strEx::blindstr(command.c_str()));
147        if (cit == commands.end())
148                return NSCAPI::returnIgnored;
149
150        std::wstring args = (*cit).second.arguments;
151        if (SETTINGS_GET_BOOL(nrpe::ALLOW_ARGS) == 1) {
152                int i=1;
153                BOOST_FOREACH(wstring arg, arguments)
154                {
155                        if (SETTINGS_GET_INT(nrpe::ALLOW_NASTY) == 0) {
156                                if (arg.find_first_of(NASTY_METACHARS) != std::wstring::npos) {
157                                        NSC_LOG_ERROR(_T("Request string contained illegal metachars!"));
158                                        return NSCAPI::returnIgnored;
159                                }
160                        }
161                        strEx::replace(args, _T("$ARG") + strEx::itos(i++) + _T("$"), arg);
162                }
163        }
164
165        NSC_DEBUG_MSG_STD(_T("Rewrote command arguments: ") + args);
166        nrpe_result_data r = execute_nrpe_command((*cit).second, args);
167        message = r.text;
168        return r.result;
169}
170
171int NRPEClient::commandLineExec(const unsigned int argLen, TCHAR** args) {
172        try {
173
174                NRPEClient::nrpe_connection_data command_data;
175                boost::program_options::variables_map vm;
176
177                po::options_description desc("Allowed options");
178                buffer_length_ = SETTINGS_GET_INT(nrpe::PAYLOAD_LENGTH);
179                add_options(desc, command_data);
180
181                po::positional_options_description p;
182                p.add("arguments", -1);
183                po::wparsed_options parsed = basic_command_line_parser_ex<wchar_t>(argLen, args).options(desc).positional(p).run();
184                po::store(parsed, vm);
185                po::notify(vm);
186                command_data.parse_arguments();
187
188                if (vm.count("help")) {
189                        std::cout << desc << "\n";
190                        return 1;
191                }
192                nrpe_result_data result = execute_nrpe_command(command_data, command_data.arguments);
193                std::wcout << result.text << std::endl;
194                return result.result;
195        } catch (boost::program_options::validation_error &e) {
196                std::cout << e.what() << std::endl;
197        } catch (...) {
198                std::cout << "Unknown exception parsing command line" << std::endl;
199        }
200        return NSCAPI::returnUNKNOWN;
201}
202NRPEClient::nrpe_result_data NRPEClient::execute_nrpe_command(nrpe_connection_data con, std::wstring arguments) {
203        try {
204                nrpe::packet packet;
205                if (!con.no_ssl) {
206#ifdef USE_SSL
207                        packet = send_ssl(con.host, con.port, con.timeout, nrpe::packet::make_request(con.get_cli(arguments), con.buffer_length));
208#else
209                        return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("SSL support not available (compiled without USE_SSL)!"));
210#endif
211                } else
212                        packet = send_nossl(con.host, con.port, con.timeout, nrpe::packet::make_request(con.get_cli(arguments), con.buffer_length));
213                return nrpe_result_data(packet.getResult(), packet.getPayload());
214        } catch (nrpe::nrpe_packet_exception &e) {
215                return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("NRPE Packet errro: ") + e.getMessage());
216        } catch (std::runtime_error &e) {
217                return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("Socket error: ") + boost::lexical_cast<std::wstring>(e.what()));
218        } catch (...) {
219                return nrpe_result_data(NSCAPI::returnUNKNOWN, _T("Unknown error -- REPORT THIS!"));
220        }
221}
222/*
223NRPEPacket NRPEClient::send_ssl(std::wstring host, int port, int timeout, NRPEPacket packet)
224{
225#ifndef USE_SSL
226        return send_nossl(host, port, timeout, packet);
227#else
228       
229        simpleSSL::Socket socket(true);
230        socket.connect(host, port);
231        NSC_DEBUG_MSG_STD(_T(">>>length: ") + strEx::itos(packet.getBufferLength()));
232        socket.sendAll(packet.getBuffer(), packet.getBufferLength());
233        simpleSocket::DataBuffer buffer;
234        socket.readAll(buffer, packet.getBufferLength());
235        NSC_DEBUG_MSG_STD(_T("<<<length: ") + strEx::itos(buffer.getLength()));
236        packet.readFrom(buffer.getBuffer(), buffer.getLength());
237        return packet;
238       
239
240        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
241        ctx.use_tmp_dh_file("d:\\nrpe_512.pem");
242        ctx.set_verify_mode(boost::asio::ssl::context::verify_peer);
243        nrpe_ssl_socket socket(io_service, ctx, host, port);
244        //socket.
245
246#endif
247}
248*/
249using boost::asio::ip::tcp;
250
251void set_result(boost::optional<boost::system::error_code>* a, boost::system::error_code b)
252{
253        a->reset(b);
254}
255template <typename AsyncReadStream, typename RawSocket, typename MutableBufferSequence>
256void read_with_timeout(AsyncReadStream& sock, RawSocket& rawSocket, const MutableBufferSequence& buffers, boost::posix_time::time_duration duration)
257{
258        boost::optional<boost::system::error_code> timer_result;
259        boost::asio::deadline_timer timer(sock.io_service());
260        timer.expires_from_now(duration);
261        timer.async_wait(boost::bind(set_result, &timer_result, _1));
262
263        boost::optional<boost::system::error_code> read_result;
264        async_read(sock, buffers, boost::bind(set_result, &read_result, _1));
265
266        sock.io_service().reset();
267        while (sock.io_service().run_one())
268        {
269                if (read_result)
270                        timer.cancel();
271                else if (timer_result)
272                        rawSocket.close();
273        }
274
275        if (*read_result)
276                throw boost::system::system_error(*read_result);
277}
278
279template <typename AsyncWriteStream, typename RawSocket, typename MutableBufferSequence>
280void write_with_timeout(AsyncWriteStream& sock, RawSocket& rawSocket, const MutableBufferSequence& buffers, boost::posix_time::time_duration duration)
281{
282        boost::optional<boost::system::error_code> timer_result;
283        boost::asio::deadline_timer timer(sock.io_service());
284        timer.expires_from_now(duration);
285        timer.async_wait(boost::bind(set_result, &timer_result, _1));
286
287        boost::optional<boost::system::error_code> read_result;
288        async_write(sock, buffers, boost::bind(set_result, &read_result, _1));
289
290        sock.io_service().reset();
291        while (sock.io_service().run_one())
292        {
293                if (read_result)
294                        timer.cancel();
295                else if (timer_result)
296                        rawSocket.close();
297        }
298
299        if (*read_result)
300                throw boost::system::system_error(*read_result);
301}
302
303class nrpe_socket : public boost::noncopyable {
304public:
305        tcp::socket socket_;
306
307public:
308        nrpe_socket(boost::asio::io_service &io_service, std::wstring host, int port) : socket_(io_service) {
309                tcp::resolver resolver(io_service);
310                tcp::resolver::query query(to_string(host), to_string(port));
311                //tcp::resolver::query query("www.medin.name", "80");
312                //tcp::resolver::query query("test_server", "80");
313
314                tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
315                tcp::resolver::iterator end;
316
317                boost::system::error_code error = boost::asio::error::host_not_found;
318                while (error && endpoint_iterator != end)
319                {
320                        tcp::resolver::endpoint_type ep = *endpoint_iterator;
321                        NSC_DEBUG_MSG_STD(_T("Attempting to connect to: ") + to_wstring(ep.address().to_string()) + _T(":") + to_wstring(ep.port()));
322                        socket_.close();
323                        socket_.connect(*endpoint_iterator++, error);
324                }
325                if (error)
326                        throw boost::system::system_error(error);
327        }
328        ~nrpe_socket() {
329                socket_.close();
330        }
331
332        void send(nrpe::packet &packet, boost::posix_time::seconds timeout) {
333                std::vector<char> buf(packet.get_packet_length());
334                write_with_timeout(socket_, socket_, boost::asio::buffer(packet.create_buffer(), packet.get_packet_length()), timeout);
335        }
336        nrpe::packet recv(const nrpe::packet &packet, boost::posix_time::seconds timeout) {
337                std::vector<char> buf(packet.get_packet_length());
338                std::cout <<  "About to read: " << buf.size() << std::endl;
339                read_with_timeout(socket_, socket_, boost::asio::buffer(buf), timeout);
340                std::cout <<  "Read data: " << buf.size() << std::endl;
341                return nrpe::packet(&buf[0], buf.size(), packet.get_payload_length());
342        }
343};
344#ifdef USE_SSL
345
346class nrpe_ssl_socket {
347
348private:
349        boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
350public:
351        nrpe_ssl_socket(boost::asio::io_service &io_service, boost::asio::ssl::context &ctx, std::wstring host, int port) : socket_(io_service, ctx) {
352                NSC_LOG_CRITICAL(_T("Looking up..."));
353                tcp::resolver resolver(io_service);
354                tcp::resolver::query query(to_string(host), to_string(port));
355                //tcp::resolver::query query("www.medin.name", "80");
356                //tcp::resolver::query query("test_server", "80");
357
358                tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
359                tcp::resolver::iterator end;
360
361                boost::system::error_code error = boost::asio::error::host_not_found;
362                NSC_LOG_CRITICAL(_T("Connecting..."));
363                while (error && endpoint_iterator != end)
364                {
365                        tcp::resolver::endpoint_type ep = *endpoint_iterator;
366                        NSC_DEBUG_MSG_STD(_T("Attempting to connect to: ") + to_wstring(ep.address().to_string()) + _T(":") + to_wstring(ep.port()));
367                        socket_.lowest_layer().close();
368                        socket_.lowest_layer().connect(*endpoint_iterator++, error);
369                }
370                if (error)
371                        throw boost::system::system_error(error);
372                NSC_LOG_CRITICAL(_T("Connected..."));
373
374                NSC_LOG_CRITICAL(_T("Handshaking..."));
375                //socket_.handshake(boost::asio::ssl::stream_base::client);
376                socket_.handshake(boost::asio::ssl::stream_base::client);
377                NSC_LOG_CRITICAL(_T("Handshook...") + strEx::itos(error.value()));
378
379        }
380
381        void send(nrpe::packet &packet, boost::posix_time::seconds timeout) {
382                std::vector<char> buf(packet.get_packet_length());
383                write_with_timeout(socket_, socket_.lowest_layer(), boost::asio::buffer(packet.create_buffer(), packet.get_packet_length()), timeout);
384        }
385        nrpe::packet recv(const nrpe::packet &packet, boost::posix_time::seconds timeout) {
386                std::vector<char> buf(packet.get_packet_length());
387                read_with_timeout(socket_, socket_.lowest_layer(), boost::asio::buffer(buf), timeout);
388                return nrpe::packet(&buf[0], buf.size(), packet.get_payload_length());
389        }
390};
391nrpe::packet NRPEClient::send_ssl(std::wstring host, int port, int timeout, nrpe::packet packet)
392{
393        boost::asio::io_service io_service;
394        boost::asio::ssl::context ctx(io_service, boost::asio::ssl::context::sslv23);
395        SSL_CTX_set_cipher_list(ctx.impl(), "ADH");
396        ctx.use_tmp_dh_file(to_string(cert_));
397        ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
398        nrpe_ssl_socket socket(io_service, ctx, host, port);
399        socket.send(packet, boost::posix_time::seconds(timeout));
400        return socket.recv(packet, boost::posix_time::seconds(timeout));
401}
402#endif
403
404nrpe::packet NRPEClient::send_nossl(std::wstring host, int port, int timeout, nrpe::packet packet)
405{
406        boost::asio::io_service io_service;
407        nrpe_socket socket(io_service, host, port);
408        socket.send(packet, boost::posix_time::seconds(timeout));
409        return socket.recv(packet, boost::posix_time::seconds(timeout));
410}
411
412/*
413NRPEPacket NRPEClient::send_nossl(std::wstring host, int port, int timeout, NRPEPacket packet)
414{
415        unsigned char dh512_p[] = {
416                0xCF, 0xFF, 0x65, 0xC2, 0xC8, 0xB4, 0xD2, 0x68, 0x8C, 0xC1, 0x80, 0xB1,
417                0x7B, 0xD6, 0xE8, 0xB3, 0x62, 0x59, 0x62, 0xED, 0xA7, 0x45, 0x6A, 0xF8,
418                0xE9, 0xD8, 0xBE, 0x3F, 0x38, 0x42, 0x5F, 0xB2, 0xA5, 0x36, 0x03, 0xD3,
419                0x06, 0x27, 0x81, 0xC8, 0x9B, 0x88, 0x50, 0x3B, 0x82, 0x3D, 0x31, 0x45,
420                0x2C, 0xB4, 0xC5, 0xA5, 0xBE, 0x6A, 0xE3, 0x2E, 0xA6, 0x86, 0xFD, 0x6A,
421                0x7E, 0x1E, 0x6A, 0x73,
422        };
423        unsigned char dh512_g[] = { 0x02, };
424
425        DH *dh_2 = DH_new();
426        dh_2->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
427        dh_2->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
428
429        FILE *outfile = fopen("d:\\nrpe_512.pem", "w");
430        PEM_write_DHparams(outfile, dh_2);
431        PEM_write_DHparams(stdout, dh_2);
432        fclose(outfile);
433
434        nrpe_socket socket(host, port);
435        socket.send(packet, boost::posix_time::seconds(timeout));
436        return socket.recv(packet, boost::posix_time::seconds(timeout));
437}
438*/
439
440
441
442
443
444NSC_WRAPPERS_MAIN_DEF(gNRPEClient);
445NSC_WRAPPERS_IGNORE_MSG_DEF();
446NSC_WRAPPERS_HANDLE_CMD_DEF(gNRPEClient);
447NSC_WRAPPERS_HANDLE_CONFIGURATION(gNRPEClient);
448NSC_WRAPPERS_CLI_DEF(gNRPEClient);
449
450
451MODULE_SETTINGS_START(NRPEClient, _T("NRPE Listener configuration"), _T("..."))
452
453PAGE(_T("NRPE Listsner configuration"))
454
455ITEM_EDIT_TEXT(_T("port"), _T("This is the port the NRPEClient.dll will listen to."))
456ITEM_MAP_TO(_T("basic_ini_text_mapper"))
457OPTION(_T("section"), _T("NRPE"))
458OPTION(_T("key"), _T("port"))
459OPTION(_T("default"), _T("5666"))
460ITEM_END()
461
462ITEM_CHECK_BOOL(_T("allow_arguments"), _T("This option determines whether or not the NRPE daemon will allow clients to specify arguments to commands that are executed."))
463ITEM_MAP_TO(_T("basic_ini_bool_mapper"))
464OPTION(_T("section"), _T("NRPE"))
465OPTION(_T("key"), _T("allow_arguments"))
466OPTION(_T("default"), _T("false"))
467OPTION(_T("true_value"), _T("1"))
468OPTION(_T("false_value"), _T("0"))
469ITEM_END()
470
471ITEM_CHECK_BOOL(_T("allow_nasty_meta_chars"), _T("This might have security implications (depending on what you do with the options)"))
472ITEM_MAP_TO(_T("basic_ini_bool_mapper"))
473OPTION(_T("section"), _T("NRPE"))
474OPTION(_T("key"), _T("allow_nasty_meta_chars"))
475OPTION(_T("default"), _T("false"))
476OPTION(_T("true_value"), _T("1"))
477OPTION(_T("false_value"), _T("0"))
478ITEM_END()
479
480ITEM_CHECK_BOOL(_T("use_ssl"), _T("This option will enable SSL encryption on the NRPE data socket (this increases security somwhat."))
481ITEM_MAP_TO(_T("basic_ini_bool_mapper"))
482OPTION(_T("section"), _T("NRPE"))
483OPTION(_T("key"), _T("use_ssl"))
484OPTION(_T("default"), _T("true"))
485OPTION(_T("true_value"), _T("1"))
486OPTION(_T("false_value"), _T("0"))
487ITEM_END()
488
489PAGE_END()
490ADVANCED_PAGE(_T("Access configuration"))
491
492ITEM_EDIT_OPTIONAL_LIST(_T("Allow connection from:"), _T("This is the hosts that will be allowed to poll performance data from the NRPE server."))
493OPTION(_T("disabledCaption"), _T("Use global settings (defined previously)"))
494OPTION(_T("enabledCaption"), _T("Specify hosts for NRPE server"))
495OPTION(_T("listCaption"), _T("Add all IP addresses (not hosts) which should be able to connect:"))
496OPTION(_T("separator"), _T(","))
497OPTION(_T("disabled"), _T(""))
498ITEM_MAP_TO(_T("basic_ini_text_mapper"))
499OPTION(_T("section"), _T("NRPE"))
500OPTION(_T("key"), _T("allowed_hosts"))
501OPTION(_T("default"), _T(""))
502ITEM_END()
503
504PAGE_END()
505MODULE_SETTINGS_END()
Note: See TracBrowser for help on using the repository browser.