blob: cb23819e8501826ef5f8449dda479c6f4cf649bb [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2003-2009 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2003-2007 Determina Corp. */
/* eventlog.c - Windows specific event logging issues */
#include "../globals.h"
#include <windows.h>
#include <tchar.h>
#include "../utils.h"
#include "events.h" /* generated from message file */
#include "ntdll.h"
/* Types for Named pipe communication to the Event Log */
#define NONCE_LENGTH 20
#define MAX_MESSAGE_SIZE 1024 /* total message size communicated to the eventlog */
/* Connection state */
typedef struct eventlog_state_t {
HANDLE eventlog_pipe;
HANDLE eventlog_completion; /* used for synchronization */
uint message_seq; /* message sequence number */
char nonce[NONCE_LENGTH]; /* nonce received from server on handshake */
mutex_t eventlog_mutex; /* sync persistent thread shared logging connection */
/* place buffers here to save stack space, used by [de]register and report
* all of whom protect them with the above lock, this structure is single
* instance static anyways so not wasting much memory doing it this way */
char outbuf[MAX_MESSAGE_SIZE];
size_t outlen;
char buf[MAX_MESSAGE_SIZE];
int request_length;
} eventlog_state_t;
/* writes a message to the Windows Event Log */
static void os_eventlog(syslog_event_type_t priority, uint message_id,
uint substitutions_num, char **arguments,
size_t size_data, char *raw_data);
/* Custom logfile key and properties */
/* This enables administrators to control the size of the log file */
/* and we can attach SACLs for security purposes, without affecting other applications. */
// Make sure the registry key is all set up, maybe better done in the installer?
// The minimum we need:
/* addsource.reg:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Araksha]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Araksha\DynamoRIO]
"TypesSupported"=dword:00000007
"EventMessageFile"="C:\\cygwin\\home\\vlk\\exports\\x86_win32_dbg\\dynamorio.dll"
*/
/* sets the values for the already existing event source key */
static int
set_event_source_registry_values()
{
int res;
HANDLE heventsource = reg_open_key(L_EVENT_SOURCE_KEY, KEY_SET_VALUE);
/* the message file is in our main dll */
char *message_file = get_dynamorio_library_path();
wchar_t wide_message_file[MAXIMUM_PATH];
if (!heventsource) {
return 0;
}
ASSERT(message_file && strlen(message_file) < MAXIMUM_PATH);
_snwprintf(wide_message_file, MAXIMUM_PATH, L"%S", message_file);
NULL_TERMINATE_BUFFER(wide_message_file);
// FIXME: BUFOV? is the conversion locale dependent
res = reg_set_dword_key_value(heventsource,
L"TypesSupported", // which messages can go in
EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE);
res |= reg_set_dword_key_value(heventsource,
L"CategoryCount", // # of event categories supported
0);
/* REG_EXPAND_SZ can contain a level of "indirection" in the form of a
system variable that can be resolved at the time of use of the
entry. For example: EventMessageFile="%WINDIR%\\dynamorio.dll"
*/
//FIXME: I'd rather set the full REG_EXPAND_SZ to be prepared
res |= reg_set_key_value(heventsource,
L"EventMessageFile",
/* should be the name of our DLL (or RLL if we put in
* a separate file) */
wide_message_file);
res |= reg_set_key_value(heventsource,
L"CategoryMessageFile",
wide_message_file);
/* we don't use these
DisplayNameFile
DisplayNameID
ParameterMessageFile
*/
reg_close_key(heventsource);
return 1;
}
/* returns 1 on successfully setting up the registry variables for an event source */
static int
init_registry_source(void)
{
static int initialized = 0;
/* FIXME: we assume no one should have access to modify after we check */
/* FIXME: may want to register for notifications .. */
/* FIXME: if we fail we'll do this over and over for each event .. */
/* use our registry routines to avoid Win32 reentrancy issues */
/* FIXME: let's do it with access rights only as needed- I got fed up at one point*/
if (!initialized) {
/* first make sure all keys are created */
HANDLE heventsource = reg_open_key(L_EVENT_SOURCE_KEY, KEY_READ | KEY_WRITE);
HANDLE heventlogroot = 0;
HANDLE heventlog = 0;
if (!heventsource) { /* we're not in */
/* test and create eventlog key */
heventlog = reg_open_key(L_EVENT_LOG_KEY, KEY_READ | KEY_WRITE);
// KEY_READ == KEY_QUERY_VALUE | KEY_NOTIFY |
// KEY_ENUMERATE_SUB_KEYS seems too strong
if (!heventlog) {
heventlogroot = reg_open_key(L_EVENTLOG_REGISTRY_KEY,
KEY_READ | KEY_WRITE);
if (!heventlogroot) {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Registration failure. Could not open root %ls.",
L_EVENTLOG_REGISTRY_KEY);
return 0;
}
heventlog = reg_create_key(heventlogroot, L_EVENT_LOG_NAME,
KEY_ALL_ACCESS);
}
if (!heventlog) {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Could not create event log key %s.", EVENTLOG_NAME);
return 0;
}
/* obviously we'll need SET_VALUE later but to keep the logic simple we take minimal here */
heventsource = reg_create_key(heventlog, L_EVENT_SOURCE_NAME, KEY_QUERY_VALUE);
if (heventlog) {
reg_close_key(heventlog);
}
if (heventlogroot) {
reg_close_key(heventlogroot);
}
}
if (!heventsource) {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Could not create event source key %s.", EVENTSOURCE_NAME);
return 0;
}
reg_close_key(heventsource);
initialized = set_event_source_registry_values();
}
return initialized;
}
#define MAX_SYSLOG_ARGS 6 /* increase if necessary */
/* collect arguments in an array and pass along */
void os_syslog(syslog_event_type_t priority, uint message_id, uint substitutions_num,
va_list vargs)
{
uint arg;
char *arg_arr[MAX_SYSLOG_ARGS];
// pointer to raw data TODO: SYSLOG_DATA entry point that also adds data arguments
char *other_data = "";
size_t size_data = strlen(other_data); /* 0 - for no data */
ASSERT(substitutions_num < MAX_SYSLOG_ARGS);
for(arg = 0; arg < substitutions_num; arg++) {
arg_arr[arg] = va_arg(vargs, char*);
}
/* don't need to check syslog, mask, caller is responsible for
* checking the mask and synchronizing the options
*/
os_eventlog(priority, message_id, substitutions_num, arg_arr, size_data, other_data);
}
/* Here starts the gross hack for direct message passing to the EventLog service */
/* Macros for adding variable length fields to a message buffer
p should point to the current position in the string appended to
pend points to the first location after the end of the buffer */
/* Modifies p */
#define FIELD(p, pend, type, val) do { \
if (!(p) || (p) + sizeof(type) > (pend)) \
return 0; \
*(type*)(p) = (type)(val); \
(p) += sizeof(type); \
} while (0)
/* pass &p */ \
#define VARFIELD(pp, pend, val, len) do { \
if (!(*(pp)) || (*(pp)) + (len) > (pend)) \
return 0; \
memcpy((*(pp)), (val), (len)); \
*(pp) += (len); \
} while (0)
/* The advapi functions don't actually zero out the padding */
/* Modifies p (i.e. *pp) */
#define PADDING(pp, pend, len, boundary) do { \
int skip = (int)PAD((len), (boundary)); \
if (!(*(pp)) || (*(pp)) + (skip) > (pend)) \
return 0; \
while (skip--) \
*(*(pp))++ = '\0'; \
} while(0)
/* Encodes an ASCIIZ string into some weird format */
/* Example "\011\0\012\0""03B\0""\12\0\0\0DynamoRio\0\0\0"
CHECK: Is this a documented M$ string representation?
It seems like there is a lot of redundancy in the encoding,
but it maybe useful in later reincarnations of the message.
This is in fact a plain UNICODE_STRING.
*/
static inline
char*
append_string(char **pp /* INOUT */, char *pend, char *str)
{
size_t length = strlen(str) + 1;
WORD len;
ASSERT_TRUNCATE(len, ushort, length); /* ushort == WORD */
len = (WORD) length;
FIELD(*pp, pend, WORD, len-1);
FIELD(*pp, pend, WORD, len);
FIELD(*pp, pend, void*, str);
FIELD(*pp, pend, DWORD, len);
VARFIELD(pp, pend, str, len);
PADDING(pp, pend, len, sizeof(DWORD));
return *pp;
}
#define HEADER_SIZE 24
#define HEADER_OFFSET 28
static inline
char*
prepend_header(char *p, char *pend,
char *header, int length, int sequence,
DWORD unknown)
{
if (!p)
return NULL;
VARFIELD(&p, pend, header, 8);
FIELD(p, pend, DWORD, length);
FIELD(p, pend, DWORD, sequence);
FIELD(p, pend, DWORD, length - HEADER_SIZE);
FIELD(p, pend, DWORD, unknown);
FIELD(p, pend, DWORD, 0);
return p;
}
/* FIXME: this value needs to be decoded using Ethereal too. See case 5655. */
#define EVENTLOG "\20\0\0\0" /* always 16 */
/* The first byte of the hello_message string should be \x05, but this
* triggers a false positive in McAfee. That's why that byte is set to
* RPC_VERSION_BOGUS and replaced with RPC_VERSION_5 before it is used.
* See case 5002 for more details.*/
#define RPC_VERSION_BOGUS "\xFF" /* must be a string */
#define RPC_VERSION_5 '\x05' /* must be a character */
/* advapi sends this message for several days with different computer names,
if I break the protocol then its hello request starts with H\0\0\0\5... */
static char hello_message[] = /* DCE RPC request, decoded by Ethereal */
RPC_VERSION_BOGUS /* Version: Should be 5, but we set it to a bogus value
* because of a false positive in McAfee. See case 5002 */
"\x00" /* Version (minor): 0 */
"\x0B" /* Packet type: Bind (11) */
"\x03" /* Packet Flags: 0x03 */
"\x10\x00\x00\x00" /* Data Representation: 10000000 */
"\x48\x00" /* Frag Length: 72 */
"\x00\x00" /* Auth Length: 0 */
"\x01\x00\x00\x00" /* Call ID: 1 */
"\xB8\x10" /* Max Xmit Frag: 4280 */
"\xB8\x10" /* Max Recv Frag: 4280 */
"\x00\x00\x00\x00" /* Assoc Group: 0x00000000 */
"\x01\x00\x00\x00" /* Num Ctx Items: 1 */
"\x00\x00" /* Context ID: 0 */
"\x01\x00" /* Num Trans Items: 1 */
"\xDC\x3F\x27\x82\x2A\xE3\xC3\x18" /* Interface UUID: */
"\x3F\x78\x82\x79\x29\xDC\x23\xEA" /* 82273fdc-e32a-18c3-3f78-827929dc23ea */
"\x00\x00" /* Interface Ver: 0 */
"\x00\x00" /* Interface Ver Minor: 0 */
"\x04\x5D\x88\x8A\xEB\x1C\xC9\x11" /* Transfer Syntax: */
"\x9F\xE8\x08\x00\x2B\x10\x48\x60" /* 8a885d04-1ceb-11c9-9fe8-08002b104860 */
"\x02\x00\x00\x00";
/* We ignore this response to the hello message:
*
* // DCE RPC response, decoded by Ethereal
* "\x05" // Version: 5
* "\x00" // Version (minor): 0
* "\x0C" // Packet type: Bind_ack (12)
* "\x03" // Packet Flags: 0x03
* "\x10\x00\x00\x00" // Data Representation: 10000000
* "\x44\x00" // Frag Length: 68
* "\x00\x00" // Auth Length: 0
* "\x01\x00\x00\x00" // Call ID: 1
* "\xB8\x10" // Max Xmit Frag: 4280
* "\xB8\x10" // Max Recv Frag: 4280
* "\x54\x13\x01\x00" // Assoc Group: 0x00011354 (may vary)
* "\x0D\x00" // Scndry Addr len: 13
* "\\PIPE\\ntsvcs\x00" // Scndry Addr: \PIPE\ntsvcs
* "\x00" // for alignment? (gets to 4 byte alignment)
* "\x01\x00\x00\x00" // Num results: 1
* "\x00\x00\x00\x00" // Ack result: Acceptance (0)
* "\x04\x5D\x88\x8A\xEB\x1C\xC9\x11" // Transfer Syntax:
* "\x9F\xE8\x08\x00\x2B\x10\x48\x60" // 8a885d04-1ceb-11c9-9fe8-08002b104860
* "\x02\x00\x00\x00"
*
* Note that the hello response has changed slightly in Vista (from hand
* comparison) :
* ...
* ! "\x0F\x00" // Scndry Addr len: 15
* ! "\\PIPE\\eventlog\x00" // Scndry Addr: \PIPE\eventlog
* ! "\x00\x73\x00" // for alignment? (gets to 4 byte alignment)
* ...
*/
#define REPORT "\5\0\0\3"
#define REGISTER_UNKNOWN_HEADER *(int*)"\0\0\17\0"
#define REPORT_UNKNOWN_HEADER *(int*)"\0\0\22\0"
#define DEREGISTER_UNKNOWN_HEADER *(int*)"\0\0\3\0"
/* TODO: The client can talk to a named pipe server on a remote machine,
then we will be able to get messages out even before the local services are started! */
#define EVENTLOG_NAMED_PIPE L"\\??\\PIPE\\EVENTLOG"
// debugging facility
#ifdef DEBUG
#define PRINT(form, arg) LOG(GLOBAL, LOG_TOP, 3, form, arg)
static void
print_buffer_as_bytes (unsigned char *buf, size_t len)
{
size_t i;
int nonprint = 0;
PRINT ("%s", "\"");
for (i=0; i<len; i++) {
if (isdigit (buf[i]) && nonprint) {
PRINT ("%s", "\"\""); // to make \01 into \0""1
}
if (buf[i] == '\\')
PRINT ("%s", "\\");
if (isprint (buf[i])) {
PRINT ("%c", buf[i]);
nonprint = 0;
} else {
PRINT ("\\%o", buf[i]);
nonprint = 1;
}
}
PRINT ("%s", "\"");
PRINT ("%s", ";\n");
}
#undef PRINT
#endif /* DEBUG */
/* see comments above, the response message length changed in Vista */
#define HELLO_RESPONSE_LENGTH \
(get_os_version() < WINDOWS_VERSION_VISTA ? 68U : 72U)
#define REGISTER_RESPONSE_LENGTH 48
#define REPORT_RESPONSE_LENGTH 36
/* returns 0 on failure */
/* must hold the eventlog mutex */
int
eventlog_register(eventlog_state_t *evconnection)
{
char *p;
ASSERT(evconnection != NULL);
ASSERT_OWN_MUTEX(true, &evconnection->eventlog_mutex);
evconnection->eventlog_completion = create_iocompletion();
evconnection->eventlog_pipe = open_pipe(EVENTLOG_NAMED_PIPE,
evconnection->eventlog_completion);
if (!evconnection->eventlog_pipe) {
LOG(GLOBAL, LOG_TOP, 1, "Couldn't open EVENTLOG\n");
goto failed_to_register;
}
/* we can't do this in eventlog_init() b/c sometimes eventlog_register()
* is called before eventlog_init()
*/
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
/* Restore the first byte of hello_message before it is used. It was set
* to a bogus value to avoid a McAfee false positive. See case 5002. */
hello_message[0] = RPC_VERSION_5;
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
evconnection->request_length = sizeof(hello_message) - 1;
evconnection->outlen = nt_pipe_transceive(evconnection->eventlog_pipe,
hello_message,
evconnection->request_length,
evconnection->outbuf,
sizeof(evconnection->outbuf),
DYNAMO_OPTION(eventlog_timeout));
DOLOG(2, LOG_TOP, {
LOG(GLOBAL, LOG_TOP, 3, "inlen= %d; outlen = "SZFMT"\"\n",
evconnection->request_length, evconnection->outlen);
LOG(GLOBAL, LOG_TOP, 3, "char hello[] = ");
print_buffer_as_bytes((byte*)hello_message, evconnection->request_length);
LOG(GLOBAL, LOG_TOP, 3, "char hello_resp[] = ");
print_buffer_as_bytes((byte*)evconnection->outbuf, evconnection->outlen);
});
if (evconnection->outlen != HELLO_RESPONSE_LENGTH) {
LOG(GLOBAL, LOG_TOP, 1,
"eventlog_register: Mismatch on HELLO_RESPONSE outlen="SZFMT"\n",
evconnection->outlen);
goto failed_to_register;
}
/* the only expected message length, we're lenient on contents */
evconnection->message_seq = 1; /* we start counting from source registration */
p = evconnection->buf + HEADER_OFFSET;
append_string(&p, evconnection->buf + sizeof(evconnection->buf),
EVENTSOURCE_NAME);
#define REPORT_IN_LOG "Application"
/* CHECK: I don't quite get how the log name here matters for Event Viewer,
since the source is registered only under EVENTLOG_NAME subtree,
TODO: yet we may want to have our own event file, and it may matter then.*/
append_string(&p, evconnection->buf + sizeof(evconnection->buf),
REPORT_IN_LOG);
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf),
"\1\0\0\0\1\0\0\0", 8); /*UNKNOWN*/
ASSERT(p); // our buffer should be large enough
IF_X64(ASSERT_TRUNCATE(evconnection->request_length, int, p - evconnection->buf));
evconnection->request_length = (int) (p - evconnection->buf);
p = prepend_header(evconnection->buf,
evconnection->buf + sizeof(evconnection->buf),
REPORT EVENTLOG, evconnection->request_length,
evconnection->message_seq, REGISTER_UNKNOWN_HEADER);
ASSERT(p);
evconnection->message_seq++;
evconnection->outlen = nt_pipe_transceive(evconnection->eventlog_pipe,
evconnection->buf,
evconnection->request_length,
evconnection->outbuf,
sizeof(evconnection->outbuf),
DYNAMO_OPTION(eventlog_timeout));
DOLOG(2, LOG_TOP, {
LOG(GLOBAL, LOG_TOP, 3, "inlen= %d; outlen = "SZFMT"\n",
evconnection->request_length, evconnection->outlen);
LOG(GLOBAL, LOG_TOP, 3, "char reg[] = ");
print_buffer_as_bytes((byte*)evconnection->buf, evconnection->request_length);
LOG(GLOBAL, LOG_TOP, 3, "char reg_resp[] = ");
print_buffer_as_bytes((byte*)evconnection->outbuf, evconnection->outlen);
});
/* the only expected message length, we're lenient on contents */
if (evconnection->outlen != REGISTER_RESPONSE_LENGTH) {
LOG(GLOBAL, LOG_TOP, 1,
"eventlog_register: Mismatch on REGISTER_RESPONSE outlen="SZFMT"\n",
evconnection->outlen);
goto failed_to_register;
}
// we can parse the output to verify its contents, yet we care only about the nonce
memcpy(evconnection->nonce, &evconnection->outbuf[HEADER_OFFSET],
NONCE_LENGTH);
return 1;
/* on error */
failed_to_register:
if (evconnection->eventlog_completion) {
close_handle(evconnection->eventlog_completion);
evconnection->eventlog_completion = 0;
}
if (evconnection->eventlog_pipe) {
close_file(evconnection->eventlog_pipe);
evconnection->eventlog_pipe = 0;
}
return 0;
}
/* get computer name, (cached) */
char*
get_computer_name()
{
static char computer_name[MAX_COMPUTERNAME_LENGTH + 5]; /* 15 + 5 */
/* returns the Windows name of the current computer just like GetComputerName() */
if (!computer_name[0]) {
char buf[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+
sizeof(wchar_t)*(MAX_COMPUTERNAME_LENGTH + 1)]; // wide
KEY_VALUE_PARTIAL_INFORMATION* kvpi = (KEY_VALUE_PARTIAL_INFORMATION*)buf;
if (reg_query_value(L"\\Registry\\Machine\\System\\CurrentControlSet"
L"\\Control\\ComputerName\\ActiveComputerName",
L"ComputerName",
KeyValuePartialInformation,
kvpi,
sizeof(buf), 0) ==
REG_QUERY_SUCCESS) {
/* Case 8185: this reg key may not be set until after winlogon
* starts up, and our first event may be post-init as well once
* the eventlog service is up. So we may need to unprotect
* .data here.
*/
if (dynamo_initialized) {
ASSERT(check_should_be_protected(DATASEC_RARELY_PROT));
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
} else
ASSERT(!DATASEC_PROTECTED(DATASEC_RARELY_PROT));
snprintf(computer_name, sizeof(computer_name) - 1, "%*ls",
kvpi->DataLength / sizeof(wchar_t) - 1,
(wchar_t*)kvpi->Data);
/* computer_name is static so last element is zeroed */
if (dynamo_initialized)
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
}
}
return computer_name;
}
/* must hold the eventlog mutex */
static int
eventlog_report(eventlog_state_t *evconnection,
WORD severity, WORD category, DWORD message_id,
void *pSID,
uint substitutions_num, size_t raw_data_size,
char **substitutions, char *raw_data)
{
char *p;
uint i;
ULONG sec = query_time_seconds();
ASSERT_OWN_MUTEX(true, &evconnection->eventlog_mutex);
p = evconnection->buf + HEADER_OFFSET;
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf),
evconnection->nonce, NONCE_LENGTH);
p -= 4; /* overwrite timestamp? */
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD, sec);
FIELD(p, evconnection->buf + sizeof(evconnection->buf), WORD, severity);
FIELD(p, evconnection->buf + sizeof(evconnection->buf), WORD, category);
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD, message_id);
FIELD(p, evconnection->buf + sizeof(evconnection->buf), WORD,
substitutions_num);
/* FIXME: should write this constant in hex instead - \333 is not meaningful anyways
* with the following broken code we've been writing
* $SG23701 DB 0dbH, 'w', 00H
* which is 0x77db.
* We should either keep using the magic value that has worked,
* or figure out what should have really been written there.
*/
FIELD(p, evconnection->buf + sizeof(evconnection->buf), WORD,
*(WORD*)"\333w"); /* FIXME: ReservedFlags? */
IF_X64(ASSERT(CHECK_TRUNCATE_TYPE_uint(raw_data_size)));
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD,
(DWORD)raw_data_size);
append_string(&p, evconnection->buf + sizeof(evconnection->buf),
get_computer_name());
/* FIXME: This used to be type DWORD: I'm guessing that it should be widened */
IF_X64(ASSERT_NOT_TESTED());
FIELD(p, evconnection->buf + sizeof(evconnection->buf), void*, pSID);
if (pSID) {
// FIXME: dump a SID in binary format
// FIXME: the actual structure order seems to be
// WORD(sub_authorities_num),
// 48 bit authority value,
// sub_authorities_num * ( 48 bit sub-authority values)
}
// FIXME: these don't seem to be either offsets nor pointers
// but are some function of the number of substitutions
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD,
*(DWORD*)"\230y\23\0"); /* FIXME pointer placeholder */
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD,
substitutions_num);
for(i=0; i<substitutions_num; i++) {
/* FIXME unknown pointer placeholder */
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD,
*(DWORD*)"\210y\23\0");
}
for(i=0; i<substitutions_num; i++) {
append_string(&p, evconnection->buf + sizeof(evconnection->buf),
substitutions[i]);
}
/* just the pointer */
/* FIXME: This used to be type DWORD: I'm guessing that it should be widened */
IF_X64(ASSERT_NOT_TESTED());
FIELD(p, evconnection->buf + sizeof(evconnection->buf), char*, raw_data);
FIELD(p, evconnection->buf + sizeof(evconnection->buf), DWORD,
raw_data_size);
LOG(GLOBAL, LOG_TOP, 3, "datalen=%d data= %*s\n", raw_data_size,
raw_data_size, raw_data);
if (raw_data_size) {
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf), raw_data,
raw_data_size); /* now the data */
PADDING(&p, evconnection->buf + sizeof(evconnection->buf),
raw_data_size, sizeof(DWORD));
}
/* FIXME: extra padding
It seems like the server can handle more but not less padding */
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf), "\0\0\0\0", 4);
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf), "\0\0\0\0", 4);
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf), "\0\0\0\0", 4);
IF_X64(ASSERT_TRUNCATE(evconnection->request_length, int, p - evconnection->buf));
evconnection->request_length = (int) (p - evconnection->buf);
p = prepend_header(evconnection->buf,
evconnection->buf + sizeof(evconnection->buf),
REPORT EVENTLOG, evconnection->request_length,
evconnection->message_seq, REPORT_UNKNOWN_HEADER);
ASSERT(p);
evconnection->message_seq++;
evconnection->outlen = nt_pipe_transceive(evconnection->eventlog_pipe,
evconnection->buf,
evconnection->request_length,
evconnection->outbuf,
sizeof(evconnection->outbuf),
DYNAMO_OPTION(eventlog_timeout));
DOLOG(2, LOG_TOP, {
LOG(GLOBAL, LOG_TOP, 3, "inlen= %d; outlen = "SZFMT"\n",
evconnection->request_length, evconnection->outlen);
LOG(GLOBAL, LOG_TOP, 3, "char report[] = ");
print_buffer_as_bytes((byte*)evconnection->buf, evconnection->request_length);
LOG(GLOBAL, LOG_TOP, 3, "char report_resp[] = ");
print_buffer_as_bytes((byte*)evconnection->outbuf, evconnection->outlen);
if (evconnection->outbuf[2]=='\3') {
LOG(GLOBAL, LOG_TOP, 2, "//5 0 3 3 is bad news\n");
}
});
/* the only expected message length, we're lenient on contents */
if (evconnection->outlen != REPORT_RESPONSE_LENGTH) {
LOG(GLOBAL, LOG_TOP, 1,
"WARNING: Mismatch on REPORT_RESPONSE outlen=:"SZFMT"\n",
evconnection->outlen);
return 0;
}
return 1;
}
/* must hold the eventlog mutex */
uint
eventlog_deregister(eventlog_state_t *evconnection)
{
char *p;
int res;
ASSERT_OWN_MUTEX(true, &evconnection->eventlog_mutex);
p = evconnection->buf + HEADER_OFFSET;
VARFIELD(&p, evconnection->buf + sizeof(evconnection->buf),
evconnection->nonce, NONCE_LENGTH);
IF_X64(ASSERT_TRUNCATE(evconnection->request_length, int, p - evconnection->buf));
evconnection->request_length = (int) (p - evconnection->buf);
p = prepend_header(evconnection->buf,
evconnection->buf + sizeof(evconnection->buf),
REPORT EVENTLOG, evconnection->request_length,
evconnection->message_seq, DEREGISTER_UNKNOWN_HEADER);
ASSERT(p);
evconnection->outlen = nt_pipe_transceive(evconnection->eventlog_pipe,
evconnection->buf,
evconnection->request_length,
evconnection->outbuf,
sizeof(evconnection->outbuf),
DYNAMO_OPTION(eventlog_timeout));
if (evconnection->outlen != /*DE*/REGISTER_RESPONSE_LENGTH) {
LOG(GLOBAL, LOG_TOP, 1,
"WARNING: Mismatch on DEREGISTER_RESPONSE outlen="SZFMT"\n",
evconnection->outlen);
}
if (evconnection->eventlog_completion) {
close_handle(evconnection->eventlog_completion);
evconnection->eventlog_completion = 0;
}
ASSERT(evconnection->eventlog_pipe);
res = close_file(evconnection->eventlog_pipe);
evconnection->eventlog_pipe = 0;
return res;
}
/* Getting a new handle may be not very performant, and also may fail
* at unexpected times we cache session state across messages and across
* threads */
static eventlog_state_t *shared_eventlog_connection;
/* we use this if we have to syslog prior to heap being initialized */
static eventlog_state_t temp_shared_eventlog_connection;
/* separate so it can be called for pre-eventlog_init() syslogs */
static void
eventlog_alloc()
{
/* Shouldn't come in here later when would need multi-thread synch.
* Sometimes eventlog registration fails until post-init (for lsass, e.g.)
* but the alloc should happen during init regardless.
*/
ASSERT(!dynamo_initialized);
if (shared_eventlog_connection != NULL &&
shared_eventlog_connection != &temp_shared_eventlog_connection) {
/* an early syslog was post-heap-init and we are fully initialized */
return;
}
if (!dynamo_heap_initialized) {
/* No heap available, so we use our temp static struct.
* eventlog_init() will call this routine again and we'll copy
* to the heap (else condition below).
*/
ASSERT(shared_eventlog_connection == NULL);
shared_eventlog_connection = &temp_shared_eventlog_connection;
ASSIGN_INIT_LOCK_FREE(shared_eventlog_connection->eventlog_mutex,
eventlog_mutex);
} else {
eventlog_state_t *alloc = HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, eventlog_state_t,
ACCT_OTHER, PROTECTED);
if (shared_eventlog_connection == &temp_shared_eventlog_connection) {
/* transfer from the temp static structure to the heap */
memcpy(alloc, &temp_shared_eventlog_connection,
sizeof(temp_shared_eventlog_connection));
} else {
memset(alloc, 0, sizeof(*shared_eventlog_connection));
ASSIGN_INIT_LOCK_FREE(alloc->eventlog_mutex, eventlog_mutex);
}
shared_eventlog_connection = alloc;
}
}
void
eventlog_init()
{
uint res;
/* TODO: Check a persistent (registry) counter for the current application
whether to report to the system log on this run, decrement it if present */
/* syslog_mask is dynamic, so even if 0 now we init in case it changes later */
/* we call get computer name to make sure it's static buffer is initialized
* while we are still single threaded */
get_computer_name();
/* FIXME: We don't actually get our own log as intended */
/* on error we just go in the Application EventLog */
if (DYNAMO_OPTION(syslog_init) &&
!init_registry_source()) { /* update registry keys */
DOLOG_ONCE(1, LOG_TOP, {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Could not add the event source registry keys."
"Events are reported with no message files.\n");
});
}
/* may have already been allocated for early syslogs */
eventlog_alloc();
mutex_lock(&shared_eventlog_connection->eventlog_mutex);
if (!shared_eventlog_connection->eventlog_pipe) {
/* initialize thread shared connection */
res = eventlog_register(shared_eventlog_connection);
if (!res) {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Could not register event source.\n");
}
}
mutex_unlock(&shared_eventlog_connection->eventlog_mutex);
}
void
eventlog_fast_exit(void)
{
uint res = 1; /* maybe nothing to do */
mutex_lock(&shared_eventlog_connection->eventlog_mutex);
if (shared_eventlog_connection->eventlog_pipe)
res = eventlog_deregister(shared_eventlog_connection);
shared_eventlog_connection->eventlog_pipe = 0;
mutex_unlock(&shared_eventlog_connection->eventlog_mutex);
DOLOG(1, LOG_TOP, if (!res) {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: DeregisterEventSource failed.\n");
});
}
void
eventlog_slow_exit()
{
ASSERT_CURIOSITY(shared_eventlog_connection->eventlog_pipe == 0 &&
"call after eventlog_fast_exit");
/* syslog_mask is dynamic, so even if 0 now we init in case it changes later */
DELETE_LOCK(shared_eventlog_connection->eventlog_mutex);
ASSERT(shared_eventlog_connection != NULL &&
shared_eventlog_connection != &temp_shared_eventlog_connection);
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, shared_eventlog_connection, eventlog_state_t,
ACCT_OTHER, PROTECTED);
/* try to let syslogs during later cleanup go through
* FIXME: won't re-deregister in that case
*/
shared_eventlog_connection = &temp_shared_eventlog_connection;
ASSIGN_INIT_LOCK_FREE(shared_eventlog_connection->eventlog_mutex,
eventlog_mutex);
}
/* writes a message to the Windows Event Log */
static
void
os_eventlog(syslog_event_type_t priority, uint message_id,
uint substitutions_num, char **arguments,
size_t size_data, char *raw_data)
{
WORD native_priority = 0;
WORD category = 0; /* we don't use any */
uint res = 0;
/* check mask on event_type whether to log this type of message */
if (!TEST(priority, dynamo_options.syslog_mask))
return;
switch (priority) {
case SYSLOG_INFORMATION: native_priority = EVENTLOG_INFORMATION_TYPE; break;
case SYSLOG_WARNING: native_priority = EVENTLOG_WARNING_TYPE; break;
case SYSLOG_CRITICAL: /* report as error */
case SYSLOG_ERROR: native_priority = EVENTLOG_ERROR_TYPE; break;
default:
ASSERT_NOT_REACHED();
}
if (shared_eventlog_connection == NULL)
eventlog_alloc();
mutex_lock(&shared_eventlog_connection->eventlog_mutex);
if (!shared_eventlog_connection->eventlog_pipe) {
/* Retry to open connection, since may have been unable
to do that early on for system services started before EventLog */
res = eventlog_register(shared_eventlog_connection);
if (!res) {
DOLOG_ONCE(1, LOG_TOP, {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Could not register event source on second attempt.\n");
});
} else {
LOG(GLOBAL, LOG_TOP, 1, "Registered event source after program started. "
"Events may be missing. --ok\n");
}
}
if (shared_eventlog_connection->eventlog_pipe) {
// TODO: add current user SID (thread maybe impersonated)
res = eventlog_report(shared_eventlog_connection,
(WORD)native_priority,
category,
message_id,
NULL, /* pSID */
(WORD)substitutions_num,
size_data,
arguments,
raw_data);
}
mutex_unlock(&shared_eventlog_connection->eventlog_mutex);
DOLOG(1, LOG_TOP, if (!res) {
LOG(GLOBAL, LOG_TOP, 1, "WARNING: Could not report event 0x%x. \n", message_id);
});
}