1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "remote/configfileshandler.hpp"
4 #include "remote/configpackageutility.hpp"
5 #include "remote/httputility.hpp"
6 #include "remote/filterutility.hpp"
7 #include "base/exception.hpp"
8 #include "base/utility.hpp"
9 #include <boost/algorithm/string/join.hpp>
10 #include <fstream>
11 
12 using namespace icinga;
13 
14 REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler);
15 
HandleRequest(AsioTlsStream & stream,const ApiUser::Ptr & user,boost::beast::http::request<boost::beast::http::string_body> & request,const Url::Ptr & url,boost::beast::http::response<boost::beast::http::string_body> & response,const Dictionary::Ptr & params,boost::asio::yield_context & yc,HttpServerConnection & server)16 bool ConfigFilesHandler::HandleRequest(
17 	AsioTlsStream& stream,
18 	const ApiUser::Ptr& user,
19 	boost::beast::http::request<boost::beast::http::string_body>& request,
20 	const Url::Ptr& url,
21 	boost::beast::http::response<boost::beast::http::string_body>& response,
22 	const Dictionary::Ptr& params,
23 	boost::asio::yield_context& yc,
24 	HttpServerConnection& server
25 )
26 {
27 	namespace http = boost::beast::http;
28 
29 	if (request.method() != http::verb::get)
30 		return false;
31 
32 	const std::vector<String>& urlPath = url->GetPath();
33 
34 	if (urlPath.size() >= 4)
35 		params->Set("package", urlPath[3]);
36 
37 	if (urlPath.size() >= 5)
38 		params->Set("stage", urlPath[4]);
39 
40 	if (urlPath.size() >= 6) {
41 		std::vector<String> tmpPath(urlPath.begin() + 5, urlPath.end());
42 		params->Set("path", boost::algorithm::join(tmpPath, "/"));
43 	}
44 
45 	if (request[http::field::accept] == "application/json") {
46 		HttpUtility::SendJsonError(response, params, 400, "Invalid Accept header. Either remove the Accept header or set it to 'application/octet-stream'.");
47 		return true;
48 	}
49 
50 	FilterUtility::CheckPermission(user, "config/query");
51 
52 	String packageName = HttpUtility::GetLastParameter(params, "package");
53 	String stageName = HttpUtility::GetLastParameter(params, "stage");
54 
55 	if (!ConfigPackageUtility::ValidatePackageName(packageName)) {
56 		HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
57 		return true;
58 	}
59 
60 	if (!ConfigPackageUtility::ValidateStageName(stageName)) {
61 		HttpUtility::SendJsonError(response, params, 400, "Invalid stage name.");
62 		return true;
63 	}
64 
65 	String relativePath = HttpUtility::GetLastParameter(params, "path");
66 
67 	if (ConfigPackageUtility::ContainsDotDot(relativePath)) {
68 		HttpUtility::SendJsonError(response, params, 400, "Path contains '..' (not allowed).");
69 		return true;
70 	}
71 
72 	String path = ConfigPackageUtility::GetPackageDir() + "/" + packageName + "/" + stageName + "/" + relativePath;
73 
74 	if (!Utility::PathExists(path)) {
75 		HttpUtility::SendJsonError(response, params, 404, "Path not found.");
76 		return true;
77 	}
78 
79 	try {
80 		std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary);
81 		fp.exceptions(std::ifstream::badbit);
82 
83 		String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
84 		response.result(http::status::ok);
85 		response.set(http::field::content_type, "application/octet-stream");
86 		response.body() = content;
87 		response.content_length(response.body().size());
88 	} catch (const std::exception& ex) {
89 		HttpUtility::SendJsonError(response, params, 500, "Could not read file.",
90 			DiagnosticInformation(ex));
91 	}
92 
93 	return true;
94 }
95