/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Event Log Service RPC (LOGR) interface definition. */ #include #include #include #include #include #include #include #include #define LOGR_FWD +1 #define LOGR_REW -1 #define LOGR_RECORD_SIGNATURE 0x654C664C #define LOGR_PRI(p) ((p) & LOG_PRIMASK) #define LOGR_WNSTRLEN(S) ((strlen((S)) + 1) * sizeof (smb_wchar_t)) #define LOGR_MSG_DWORD_OFFSET 12 #define LOGR_MSG_WORD_OFFSET 4 /* * READ flags for EventLogRead * * EVENTLOG_SEEK_READ * The read operation proceeds from the record specified by the * dwRecordOffset parameter. This flag cannot be used with * EVENTLOG_SEQUENTIAL_READ. * * EVENTLOG_SEQUENTIAL_READ * The read operation proceeds sequentially from the last call to the * ReadEventLog function using this handle. This flag cannot be used * with EVENTLOG_SEEK_READ. * * If the buffer is large enough, more than one record can be read at * the specified seek position; you must specify one of the following * flags to indicate the direction for successive read operations. * * EVENTLOG_FORWARDS_READ * The log is read in chronological order. This flag cannot be used * with EVENTLOG_BACKWARDS_READ. * * EVENTLOG_BACKWARDS_READ * The log is read in reverse chronological order. This flag cannot be * used with EVENTLOG_FORWARDS_READ. */ #define EVENTLOG_SEQUENTIAL_READ 0x0001 #define EVENTLOG_SEEK_READ 0x0002 #define EVENTLOG_FORWARDS_READ 0x0004 #define EVENTLOG_BACKWARDS_READ 0x0008 /* * The types of events that can be logged. */ #define EVENTLOG_SUCCESS 0x0000 #define EVENTLOG_ERROR_TYPE 0x0001 #define EVENTLOG_WARNING_TYPE 0x0002 #define EVENTLOG_INFORMATION_TYPE 0x0004 #define EVENTLOG_AUDIT_SUCCESS 0x0008 #define EVENTLOG_AUDIT_FAILURE 0x0010 /* * Event Identifiers * * Event identifiers uniquely identify a particular event. Each event * source can define its own numbered events and the description strings * to which they are mapped. Event viewers can present these strings to * the user. They should help the user understand what went wrong and * suggest what actions to take. Direct the description at users solving * their own problems, not at administrators or support technicians. * Make the description clear and concise and avoid culture-specific * phrases. * * The following diagram illustrates the format of an event identifier. * * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +---+-+-+-----------------------+-------------------------------+ * |Sev|C|R| Facility | Code | * +---+-+-+-----------------------+-------------------------------+ * * Sev * Indicates the severity. This is one of the following values: * 00 - Success * 01 - Informational * 10 - Warning * 11 - Error * * C * Indicates a customer code (1) or a system code (0). * R * Reserved bit. * Facility * Facility code. * Code * Status code for the facility. */ #define EVENTID_SEVERITY_SUCCESS 0x00000000 #define EVENTID_SEVERITY_INFO 0x40000000 #define EVENTID_SEVERITY_WARNING 0x80000000 #define EVENTID_SEVERITY_ERROR 0xC0000000 #define EVENTID_SYSTEM_CODE 0x00000000 #define EVENTID_CUSTOMER_CODE 0x20000000 static int logr_s_EventLogClose(void *, ndr_xa_t *); static int logr_s_EventLogQueryCount(void *, ndr_xa_t *); static int logr_s_EventLogGetOldestRec(void *, ndr_xa_t *); static int logr_s_EventLogOpen(void *, ndr_xa_t *); static int logr_s_EventLogRead(void *, ndr_xa_t *); static ndr_stub_table_t logr_stub_table[] = { { logr_s_EventLogClose, LOGR_OPNUM_EventLogClose }, { logr_s_EventLogQueryCount, LOGR_OPNUM_EventLogQueryCount }, { logr_s_EventLogGetOldestRec, LOGR_OPNUM_EventLogGetOldestRec }, { logr_s_EventLogOpen, LOGR_OPNUM_EventLogOpen }, { logr_s_EventLogRead, LOGR_OPNUM_EventLogRead }, {0} }; static ndr_service_t logr_service = { "LOGR", /* name */ "Event Log Service", /* desc */ "\\eventlog", /* endpoint */ PIPE_NTSVCS, /* sec_addr_port */ "82273fdc-e32a-18c3-3f78-827929dc23ea", 0, /* abstract */ NDR_TRANSFER_SYNTAX_UUID, 2, /* transfer */ 0, /* no bind_instance_size */ 0, /* no bind_req() */ 0, /* no unbind_and_close() */ 0, /* use generic_call_stub() */ &TYPEINFO(logr_interface), /* interface ti */ logr_stub_table /* stub_table */ }; /* * logr_initialize * * This function registers the LOGR RPC interface with the RPC runtime * library. It must be called in order to use either the client side * or the server side functions. */ void logr_initialize(void) { (void) ndr_svc_register(&logr_service); logr_init(); } void logr_finalize(void) { logr_fini(); } /* * logr_hdlookup * * Handle lookup wrapper to validate the local service and/or manager context. */ static ndr_handle_t * logr_hdlookup(ndr_xa_t *mxa, ndr_hdid_t *id) { ndr_handle_t *hd; logr_context_t *ctx; if ((hd = ndr_hdlookup(mxa, id)) == NULL) return (NULL); if ((ctx = (logr_context_t *)hd->nh_data) == NULL) return (NULL); if (ctx->lc_source_name == NULL) return (NULL); return (hd); } /* * logr_context_data_free * * Callback to free the context data associated with local service * and/or manager context. */ static void logr_context_data_free(void *ctxp) { logr_context_t *ctx = (logr_context_t *)ctxp; if (ctx == NULL) return; free(ctx->lc_source_name); free(ctx->lc_cached_read_data->rd_log); free(ctx->lc_cached_read_data); free(ctx); ctx = NULL; } /* * logr_hdalloc * * Handle allocation wrapper to setup the local manager context. */ static ndr_hdid_t * logr_hdalloc(ndr_xa_t *mxa, char *logname) { logr_context_t *ctx; if ((ctx = malloc(sizeof (logr_context_t))) == NULL) return (NULL); bzero(ctx, sizeof (logr_context_t)); ctx->lc_source_name = strdup(logname); if (ctx->lc_source_name == NULL) { free(ctx); return (NULL); } if (logr_get_snapshot(ctx) != 0) { free(ctx->lc_source_name); free(ctx); return (NULL); } return (ndr_hdalloc(mxa, ctx)); } /* * logr_s_EventLogClose * * This is a request to close the LOGR interface specified by handle. * Free the handle and associated resources, and zero out the result * handle for the client. */ static int logr_s_EventLogClose(void *arg, ndr_xa_t *mxa) { struct logr_EventLogClose *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; if ((hd = ndr_hdlookup(mxa, id)) == NULL) { bzero(¶m->result_handle, sizeof (logr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } logr_context_data_free(hd->nh_data); ndr_hdfree(mxa, id); bzero(¶m->result_handle, sizeof (logr_handle_t)); param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } /* * logr_s_EventLogOpen * * Open the event log. Not supported yet. */ /*ARGSUSED*/ static int logr_s_EventLogOpen(void *arg, ndr_xa_t *mxa) { struct logr_EventLogOpen *param = arg; ndr_hdid_t *id = NULL; ndr_handle_t *hd; char *log_name = NULL; if (!ndr_is_admin(mxa)) { bzero(¶m->handle, sizeof (logr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } if (param->log_name.length != 0) log_name = (char *)param->log_name.str; if (!logr_is_supported(log_name)) { bzero(¶m->handle, sizeof (logr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } id = logr_hdalloc(mxa, log_name); if (id && ((hd = logr_hdlookup(mxa, id)) != NULL)) { hd->nh_data_free = logr_context_data_free; bcopy(id, ¶m->handle, sizeof (logr_handle_t)); param->status = NT_STATUS_SUCCESS; } else { bzero(¶m->handle, sizeof (logr_handle_t)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); } return (NDR_DRC_OK); } /* * logr_s_EventLogQueryCount * * take a snapshot from system log, assign it to the given handle. * return number of log entries in the snapshot as result of RPC * call. */ static int logr_s_EventLogQueryCount(void *arg, ndr_xa_t *mxa) { struct logr_EventLogQueryCount *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; logr_context_t *ctx; logr_read_data_t *data; if ((hd = logr_hdlookup(mxa, id)) == NULL) { param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } ctx = (logr_context_t *)hd->nh_data; data = ctx->lc_cached_read_data; param->rec_num = data->rd_tot_recnum; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } /* * logr_s_EventLogGetOldestRec * * Return oldest record number in the snapshot as result of RPC call. */ static int logr_s_EventLogGetOldestRec(void *arg, ndr_xa_t *mxa) { struct logr_EventLogGetOldestRec *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; logr_context_t *ctx; logr_read_data_t *data; if ((hd = logr_hdlookup(mxa, id)) == NULL) { param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } ctx = (logr_context_t *)hd->nh_data; data = ctx->lc_cached_read_data; param->oldest_rec = data->rd_log->li_idx - data->rd_tot_recnum + 1; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); } /* * logr_set_event_typeid * * Map the local system log priority to the event type and event ID * for Windows events. */ void logr_set_event_typeid(int le_pri, WORD *etype, DWORD *eid) { switch (LOGR_PRI(le_pri)) { case LOG_EMERG: case LOG_ALERT: case LOG_CRIT: case LOG_ERR: *eid = EVENTID_SEVERITY_ERROR; *etype = EVENTLOG_ERROR_TYPE; break; case LOG_WARNING: *eid = EVENTID_SEVERITY_WARNING; *etype = EVENTLOG_WARNING_TYPE; break; case LOG_NOTICE: case LOG_INFO: case LOG_DEBUG: *eid = EVENTID_SEVERITY_INFO; *etype = EVENTLOG_INFORMATION_TYPE; break; default: *eid = EVENTID_SEVERITY_SUCCESS; *etype = EVENTLOG_SUCCESS; } } /* * logr_get_entry * * Gets a log entry. */ static logr_entry_t * logr_get_entry(logr_info_t *linfo, int entno) { return (&linfo->li_entry[entno]); } /* * logr_set_logrecord * * Fill a Windows event record based on a local system log record. */ static void logr_set_logrecord(char *src_name, logr_entry_t *le, DWORD recno, logr_record_t *rec) { int srcname_len = 0, hostname_len = 0, len; int str_offs, sh_len; smb_wchar_t wcs_hostname[MAXHOSTNAMELEN]; smb_wchar_t wcs_srcname[SYS_NMLN * 2]; (void) smb_mbstowcs(wcs_srcname, src_name, strlen(src_name) + 1); srcname_len = LOGR_WNSTRLEN(src_name); /* Because, Solaris allows remote logging, need to get hostname here */ (void) smb_mbstowcs(wcs_hostname, le->le_hostname, strlen(le->le_hostname) + 1); hostname_len = LOGR_WNSTRLEN(le->le_hostname); sh_len = srcname_len + hostname_len; str_offs = LOGR_MSG_DWORD_OFFSET * sizeof (DWORD) + LOGR_MSG_WORD_OFFSET * sizeof (WORD) + sh_len; rec->Length1 = sizeof (logr_record_t); rec->Reserved = LOGR_RECORD_SIGNATURE; rec->RecordNumber = recno; rec->TimeGenerated = le->le_timestamp.tv_sec; rec->TimeWritten = le->le_timestamp.tv_sec; logr_set_event_typeid(le->le_pri, &rec->EventType, &rec->EventID); rec->NumStrings = 1; rec->EventCategory = 0; rec->ReservedFlags = 0; rec->ClosingRecordNumber = 0; rec->StringOffset = str_offs; rec->UserSidLength = 0; rec->UserSidOffset = 0; rec->DataLength = 0; rec->DataOffset = 0; bzero(rec->info, LOGR_MAXENTRYLEN); (void) memcpy(rec->info, wcs_srcname, srcname_len); (void) memcpy(rec->info + srcname_len, wcs_hostname, hostname_len); len = strlen(le->le_msg) + 1; if (len > 0) /*LINTED E_BAD_PTR_CAST_ALIGN*/ (void) smb_mbstowcs((smb_wchar_t *)(rec->info + sh_len), le->le_msg, len); rec->Length2 = sizeof (logr_record_t); } /* * logr_s_EventLogRead * * Reads a whole number of entries from system log. The function can * read log entries in chronological or reverse chronological order. */ static int logr_s_EventLogRead(void *arg, ndr_xa_t *mxa) { struct logr_EventLogRead *param = arg; ndr_hdid_t *id = (ndr_hdid_t *)¶m->handle; ndr_handle_t *hd; logr_read_data_t *rdata; logr_entry_t *le; DWORD ent_no, ent_num, ent_remain; logr_record_t *rec; BYTE *buf; int dir, ent_per_req, iter; logr_context_t *ctx; if ((hd = logr_hdlookup(mxa, id)) == NULL) { param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE); return (NDR_DRC_OK); } ctx = (logr_context_t *)hd->nh_data; rdata = ctx->lc_cached_read_data; if (rdata == NULL) { param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY); return (NDR_DRC_OK); } dir = (param->read_flags & EVENTLOG_FORWARDS_READ) ? LOGR_FWD : LOGR_REW; if (param->read_flags & EVENTLOG_SEEK_READ) rdata->rd_last_sentrec = param->rec_offset; else if (rdata->rd_first_read) /* * set last record number which is read for * the first iteration of sequential read. */ rdata->rd_last_sentrec = (dir == LOGR_FWD) ? (rdata->rd_log->li_idx - rdata->rd_tot_recnum) : rdata->rd_log->li_idx; ent_remain = (dir == LOGR_FWD) ? (rdata->rd_tot_recnum - rdata->rd_last_sentrec) : rdata->rd_last_sentrec; /* * function should return as many whole log entries as * will fit in the buffer; it should not return partial * entries, even if there is room in the buffer. */ ent_per_req = param->nbytes_to_read / sizeof (logr_record_t); if (ent_remain > ent_per_req) ent_remain = ent_per_req; if (ent_remain == 0) { /* * Send this error to Windows client so that it * can figure out that there is no more record * to read. */ param->buf = NDR_STRDUP(mxa, ""); param->sent_size = 0; param->status = NT_SC_ERROR(NT_STATUS_END_OF_FILE); return (NDR_DRC_OK); } param->buf = NDR_MALLOC(mxa, param->nbytes_to_read); buf = (BYTE *)param->buf; for (ent_num = 0, ent_no = rdata->rd_last_sentrec; ent_num < ent_remain; ent_num++, ent_no += dir) { iter = ent_no & LOGR_NMSGMASK; if (dir == LOGR_REW) iter = (ent_no - 1) & LOGR_NMSGMASK; le = logr_get_entry(rdata->rd_log, iter); /*LINTED E_BAD_PTR_CAST_ALIGN*/ rec = (logr_record_t *)buf; logr_set_logrecord(ctx->lc_source_name, le, ent_no, rec); buf += sizeof (logr_record_t); } rdata->rd_last_sentrec = ent_no; rdata->rd_first_read = 0; param->sent_size = sizeof (logr_record_t) * ent_remain; param->status = NT_STATUS_SUCCESS; return (NDR_DRC_OK); }