1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #if (defined(WIN32) || defined(_WIN32)) && !defined(_WIN32_WCE)
19 
20 #include <apr_strings.h>
21 
22 #include <log4cxx/nt/nteventlogappender.h>
23 #include <log4cxx/spi/loggingevent.h>
24 #include <log4cxx/helpers/loglog.h>
25 #include <log4cxx/level.h>
26 #include <log4cxx/helpers/stringhelper.h>
27 #include <log4cxx/helpers/transcoder.h>
28 #include <log4cxx/helpers/pool.h>
29 
30 using namespace log4cxx;
31 using namespace log4cxx::spi;
32 using namespace log4cxx::helpers;
33 using namespace log4cxx::nt;
34 
35 class CCtUserSIDHelper
36 {
37 	public:
FreeSid(SID * pSid)38 		static bool FreeSid(SID* pSid)
39 		{
40 			return ::HeapFree(GetProcessHeap(), 0, (LPVOID)pSid) != 0;
41 		}
42 
CopySid(SID ** ppDstSid,SID * pSrcSid)43 		static bool CopySid(SID * * ppDstSid, SID* pSrcSid)
44 		{
45 			bool bSuccess = false;
46 
47 			DWORD dwLength = ::GetLengthSid(pSrcSid);
48 			*ppDstSid = (SID*) ::HeapAlloc(GetProcessHeap(),
49 					HEAP_ZERO_MEMORY, dwLength);
50 
51 			if (::CopySid(dwLength, *ppDstSid, pSrcSid))
52 			{
53 				bSuccess = true;
54 			}
55 			else
56 			{
57 				FreeSid(*ppDstSid);
58 			}
59 
60 			return bSuccess;
61 		}
62 
GetCurrentUserSID(SID ** ppSid)63 		static bool GetCurrentUserSID(SID * * ppSid)
64 		{
65 			bool bSuccess = false;
66 
67 			// Pseudohandle so don't need to close it
68 			HANDLE hProcess = ::GetCurrentProcess();
69 			HANDLE hToken = NULL;
70 
71 			if (::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
72 			{
73 				// Get the required size
74 				DWORD tusize = 0;
75 				GetTokenInformation(hToken, TokenUser, NULL, 0, &tusize);
76 				TOKEN_USER* ptu = (TOKEN_USER*)new BYTE[tusize];
77 
78 				if (GetTokenInformation(hToken, TokenUser, (LPVOID)ptu, tusize, &tusize))
79 				{
80 					bSuccess = CopySid(ppSid, (SID*)ptu->User.Sid);
81 				}
82 
83 				CloseHandle(hToken);
84 				delete [] ptu;
85 			}
86 
87 			return bSuccess;
88 		}
89 };
90 
IMPLEMENT_LOG4CXX_OBJECT(NTEventLogAppender)91 IMPLEMENT_LOG4CXX_OBJECT(NTEventLogAppender)
92 
93 NTEventLogAppender::NTEventLogAppender() : hEventLog(NULL), pCurrentUserSID(NULL)
94 {
95 }
96 
NTEventLogAppender(const LogString & server,const LogString & log,const LogString & source,const LayoutPtr & layout)97 NTEventLogAppender::NTEventLogAppender(const LogString& server, const LogString& log, const LogString& source, const LayoutPtr& layout)
98 	: server(server), log(log), source(source), hEventLog(NULL), pCurrentUserSID(NULL)
99 {
100 	this->layout = layout;
101 	Pool pool;
102 	activateOptions(pool);
103 }
104 
~NTEventLogAppender()105 NTEventLogAppender::~NTEventLogAppender()
106 {
107 	finalize();
108 }
109 
110 
close()111 void NTEventLogAppender::close()
112 {
113 	if (hEventLog != NULL)
114 	{
115 		::DeregisterEventSource(hEventLog);
116 		hEventLog = NULL;
117 	}
118 
119 	if (pCurrentUserSID != NULL)
120 	{
121 		CCtUserSIDHelper::FreeSid((::SID*) pCurrentUserSID);
122 		pCurrentUserSID = NULL;
123 	}
124 }
125 
setOption(const LogString & option,const LogString & value)126 void NTEventLogAppender::setOption(const LogString& option, const LogString& value)
127 {
128 	if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SERVER"), LOG4CXX_STR("server")))
129 	{
130 		server = value;
131 	}
132 	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOG"), LOG4CXX_STR("log")))
133 	{
134 		log = value;
135 	}
136 	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SOURCE"), LOG4CXX_STR("source")))
137 	{
138 		source = value;
139 	}
140 	else
141 	{
142 		AppenderSkeleton::setOption(option, value);
143 	}
144 }
145 
activateOptions(Pool &)146 void NTEventLogAppender::activateOptions(Pool&)
147 {
148 	if (source.empty())
149 	{
150 		LogLog::warn(
151 			((LogString) LOG4CXX_STR("Source option not set for appender ["))
152 			+ name + LOG4CXX_STR("]."));
153 		return;
154 	}
155 
156 	if (log.empty())
157 	{
158 		log = LOG4CXX_STR("Application");
159 	}
160 
161 	close();
162 
163 	// current user security identifier
164 	CCtUserSIDHelper::GetCurrentUserSID((::SID**) &pCurrentUserSID);
165 
166 	addRegistryInfo();
167 
168 	LOG4CXX_ENCODE_WCHAR(wsource, source);
169 	LOG4CXX_ENCODE_WCHAR(wserver, server);
170 	hEventLog = ::RegisterEventSourceW(
171 			wserver.empty() ? NULL : wserver.c_str(),
172 			wsource.c_str());
173 
174 	if (hEventLog == NULL)
175 	{
176 		LogString msg(LOG4CXX_STR("Cannot register NT EventLog -- server: '"));
177 		msg.append(server);
178 		msg.append(LOG4CXX_STR("' source: '"));
179 		msg.append(source);
180 		LogLog::error(msg);
181 		LogLog::error(getErrorString(LOG4CXX_STR("RegisterEventSource")));
182 	}
183 }
184 
append(const LoggingEventPtr & event,Pool & p)185 void NTEventLogAppender::append(const LoggingEventPtr& event, Pool& p)
186 {
187 	if (hEventLog == NULL)
188 	{
189 		LogLog::warn(LOG4CXX_STR("NT EventLog not opened."));
190 		return;
191 	}
192 
193 	LogString oss;
194 	layout->format(oss, event, p);
195 	wchar_t* msgs = Transcoder::wencode(oss, p);
196 	BOOL bSuccess = ::ReportEventW(
197 			hEventLog,
198 			getEventType(event),
199 			getEventCategory(event),
200 			0x1000,
201 			pCurrentUserSID,
202 			1,
203 			0,
204 			(LPCWSTR*) &msgs,
205 			NULL);
206 
207 	if (!bSuccess)
208 	{
209 		LogLog::error(getErrorString(LOG4CXX_STR("ReportEvent")));
210 	}
211 }
212 
213 /*
214  * Add this source with appropriate configuration keys to the registry.
215  */
addRegistryInfo()216 void NTEventLogAppender::addRegistryInfo()
217 {
218 	DWORD disposition = 0;
219 	::HKEY hkey = 0;
220 	LogString subkey(LOG4CXX_STR("SYSTEM\\CurrentControlSet\\Services\\EventLog\\"));
221 	subkey.append(log);
222 	subkey.append(1, (logchar) 0x5C /* '\\' */);
223 	subkey.append(source);
224 	LOG4CXX_ENCODE_WCHAR(wsubkey, subkey);
225 
226 	long stat = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wsubkey.c_str(), 0, NULL,
227 			REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
228 			&hkey, &disposition);
229 
230 	if (stat == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
231 	{
232 		HMODULE hmodule = GetModuleHandleW(L"log4cxx");
233 
234 		if (hmodule == NULL)
235 		{
236 			hmodule = GetModuleHandleW(0);
237 		}
238 
239 		wchar_t modpath[_MAX_PATH];
240 		DWORD modlen = GetModuleFileNameW(hmodule, modpath, _MAX_PATH - 1);
241 
242 		if (modlen > 0)
243 		{
244 			modpath[modlen] = 0;
245 			RegSetValueExW(hkey, L"EventMessageFile", 0, REG_SZ,
246 				(LPBYTE) modpath, wcslen(modpath) * sizeof(wchar_t));
247 			RegSetValueExW(hkey, L"CategoryMessageFile", 0, REG_SZ,
248 				(LPBYTE) modpath, wcslen(modpath) * sizeof(wchar_t));
249 			DWORD typesSupported = 7;
250 			DWORD categoryCount = 6;
251 			RegSetValueExW(hkey, L"TypesSupported", 0, REG_DWORD,
252 				(LPBYTE)&typesSupported, sizeof(DWORD));
253 			RegSetValueExW(hkey, L"CategoryCount", 0, REG_DWORD,
254 				(LPBYTE)&categoryCount, sizeof(DWORD));
255 		}
256 	}
257 
258 	RegCloseKey(hkey);
259 	return;
260 }
261 
getEventType(const LoggingEventPtr & event)262 WORD NTEventLogAppender::getEventType(const LoggingEventPtr& event)
263 {
264 	int priority = event->getLevel()->toInt();
265 	WORD type = EVENTLOG_SUCCESS;
266 
267 	if (priority >= Level::INFO_INT)
268 	{
269 		type = EVENTLOG_INFORMATION_TYPE;
270 
271 		if (priority >= Level::WARN_INT)
272 		{
273 			type = EVENTLOG_WARNING_TYPE;
274 
275 			if (priority >= Level::ERROR_INT)
276 			{
277 				type = EVENTLOG_ERROR_TYPE;
278 			}
279 		}
280 	}
281 
282 	return type;
283 }
284 
getEventCategory(const LoggingEventPtr & event)285 WORD NTEventLogAppender::getEventCategory(const LoggingEventPtr& event)
286 {
287 	int priority = event->getLevel()->toInt();
288 	WORD category = 1;
289 
290 	if (priority >= Level::DEBUG_INT)
291 	{
292 		category = 2;
293 
294 		if (priority >= Level::INFO_INT)
295 		{
296 			category = 3;
297 
298 			if (priority >= Level::WARN_INT)
299 			{
300 				category = 4;
301 
302 				if (priority >= Level::ERROR_INT)
303 				{
304 					category = 5;
305 
306 					if (priority >= Level::FATAL_INT)
307 					{
308 						category = 6;
309 					}
310 				}
311 			}
312 		}
313 	}
314 
315 	return category;
316 }
317 
getErrorString(const LogString & function)318 LogString NTEventLogAppender::getErrorString(const LogString& function)
319 {
320 	Pool p;
321 	enum { MSGSIZE = 5000 };
322 
323 	wchar_t* lpMsgBuf = (wchar_t*) p.palloc(MSGSIZE * sizeof(wchar_t));
324 	DWORD dw = GetLastError();
325 
326 	FormatMessageW(
327 		FORMAT_MESSAGE_FROM_SYSTEM,
328 		NULL,
329 		dw,
330 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
331 		lpMsgBuf,
332 		MSGSIZE, NULL );
333 
334 	LogString msg(function);
335 	msg.append(LOG4CXX_STR(" failed with error "));
336 	StringHelper::toString((size_t) dw, p, msg);
337 	msg.append(LOG4CXX_STR(": "));
338 	Transcoder::decode(lpMsgBuf, msg);
339 
340 	return msg;
341 }
342 
343 #endif // WIN32
344