xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/eventlog_svc.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * Event Log Service RPC (LOGR) interface definition.
29  */
30 #include <sys/utsname.h>
31 #include <unistd.h>
32 #include <strings.h>
33 #include <libmlrpc/libmlrpc.h>
34 #include <smbsrv/libsmb.h>
35 #include <smbsrv/nmpipes.h>
36 #include <smbsrv/libmlsvc.h>
37 #include <smbsrv/ndl/eventlog.ndl>
38 
39 
40 #define	LOGR_FWD		+1
41 #define	LOGR_REW		-1
42 #define	LOGR_RECORD_SIGNATURE	0x654C664C
43 
44 #define	LOGR_PRI(p)		((p) & LOG_PRIMASK)
45 #define	LOGR_WNSTRLEN(S)	((strlen((S)) + 1) * sizeof (smb_wchar_t))
46 
47 #define	LOGR_MSG_DWORD_OFFSET	12
48 #define	LOGR_MSG_WORD_OFFSET	4
49 
50 /*
51  * READ flags for EventLogRead
52  *
53  * EVENTLOG_SEEK_READ
54  * The read operation proceeds from the record specified by the
55  * dwRecordOffset parameter. This flag cannot be used with
56  * EVENTLOG_SEQUENTIAL_READ.
57  *
58  * EVENTLOG_SEQUENTIAL_READ
59  * The read operation proceeds sequentially from the last call to the
60  * ReadEventLog function using this handle. This flag cannot be used
61  * with EVENTLOG_SEEK_READ.
62  *
63  * If the buffer is large enough, more than one record can be read at
64  * the specified seek position; you must specify one of the following
65  * flags to indicate the direction for successive read operations.
66  *
67  * EVENTLOG_FORWARDS_READ
68  * The log is read in chronological order. This flag cannot be used
69  * with EVENTLOG_BACKWARDS_READ.
70  *
71  * EVENTLOG_BACKWARDS_READ
72  * The log is read in reverse chronological order. This flag cannot be
73  * used with EVENTLOG_FORWARDS_READ.
74  */
75 #define	EVENTLOG_SEQUENTIAL_READ	0x0001
76 #define	EVENTLOG_SEEK_READ		0x0002
77 #define	EVENTLOG_FORWARDS_READ		0x0004
78 #define	EVENTLOG_BACKWARDS_READ		0x0008
79 
80 /*
81  * The types of events that can be logged.
82  */
83 #define	EVENTLOG_SUCCESS		0x0000
84 #define	EVENTLOG_ERROR_TYPE		0x0001
85 #define	EVENTLOG_WARNING_TYPE		0x0002
86 #define	EVENTLOG_INFORMATION_TYPE	0x0004
87 #define	EVENTLOG_AUDIT_SUCCESS		0x0008
88 #define	EVENTLOG_AUDIT_FAILURE		0x0010
89 
90 /*
91  * Event Identifiers
92  *
93  * Event identifiers uniquely identify a particular event. Each event
94  * source can define its own numbered events and the description strings
95  * to which they are mapped. Event viewers can present these strings to
96  * the user. They should help the user understand what went wrong and
97  * suggest what actions to take. Direct the description at users solving
98  * their own problems, not at administrators or support technicians.
99  * Make the description clear and concise and avoid culture-specific
100  * phrases.
101  *
102  * The following diagram illustrates the format of an event identifier.
103  *
104  *   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
105  *   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
106  *  +---+-+-+-----------------------+-------------------------------+
107  *  |Sev|C|R|     Facility          |               Code            |
108  *  +---+-+-+-----------------------+-------------------------------+
109  *
110  *  Sev
111  *        Indicates the severity. This is one of the following values:
112  *        00 - Success
113  *        01 - Informational
114  *        10 - Warning
115  *        11 - Error
116  *
117  *  C
118  *        Indicates a customer code (1) or a system code (0).
119  *  R
120  *        Reserved bit.
121  *  Facility
122  *        Facility code.
123  *  Code
124  *        Status code for the facility.
125  */
126 #define	EVENTID_SEVERITY_SUCCESS	0x00000000
127 #define	EVENTID_SEVERITY_INFO		0x40000000
128 #define	EVENTID_SEVERITY_WARNING	0x80000000
129 #define	EVENTID_SEVERITY_ERROR		0xC0000000
130 
131 #define	EVENTID_SYSTEM_CODE		0x00000000
132 #define	EVENTID_CUSTOMER_CODE		0x20000000
133 
134 static int logr_s_EventLogClose(void *, ndr_xa_t *);
135 static int logr_s_EventLogQueryCount(void *, ndr_xa_t *);
136 static int logr_s_EventLogGetOldestRec(void *, ndr_xa_t *);
137 static int logr_s_EventLogOpen(void *, ndr_xa_t *);
138 static int logr_s_EventLogRead(void *, ndr_xa_t *);
139 
140 static ndr_stub_table_t logr_stub_table[] = {
141 	{ logr_s_EventLogClose,		LOGR_OPNUM_EventLogClose },
142 	{ logr_s_EventLogQueryCount,	LOGR_OPNUM_EventLogQueryCount },
143 	{ logr_s_EventLogGetOldestRec,	LOGR_OPNUM_EventLogGetOldestRec },
144 	{ logr_s_EventLogOpen,		LOGR_OPNUM_EventLogOpen },
145 	{ logr_s_EventLogRead,		LOGR_OPNUM_EventLogRead },
146 	{0}
147 };
148 
149 static ndr_service_t logr_service = {
150 	"LOGR",				/* name */
151 	"Event Log Service",		/* desc */
152 	"\\eventlog",			/* endpoint */
153 	PIPE_NTSVCS,			/* sec_addr_port */
154 	"82273fdc-e32a-18c3-3f78-827929dc23ea", 0,	/* abstract */
155 	NDR_TRANSFER_SYNTAX_UUID,		2,	/* transfer */
156 	0,				/* no bind_instance_size */
157 	0,				/* no bind_req() */
158 	0,				/* no unbind_and_close() */
159 	0,				/* use generic_call_stub() */
160 	&TYPEINFO(logr_interface),	/* interface ti */
161 	logr_stub_table			/* stub_table */
162 };
163 
164 /*
165  * logr_initialize
166  *
167  * This function registers the LOGR RPC interface with the RPC runtime
168  * library. It must be called in order to use either the client side
169  * or the server side functions.
170  */
171 void
172 logr_initialize(void)
173 {
174 	(void) ndr_svc_register(&logr_service);
175 	logr_init();
176 }
177 
178 void
179 logr_finalize(void)
180 {
181 	logr_fini();
182 }
183 
184 /*
185  * logr_hdlookup
186  *
187  * Handle lookup wrapper to validate the local service and/or manager context.
188  */
189 static ndr_handle_t *
190 logr_hdlookup(ndr_xa_t *mxa, ndr_hdid_t *id)
191 {
192 	ndr_handle_t *hd;
193 	logr_context_t *ctx;
194 
195 	if ((hd = ndr_hdlookup(mxa, id)) == NULL)
196 		return (NULL);
197 
198 	if ((ctx = (logr_context_t *)hd->nh_data) == NULL)
199 		return (NULL);
200 
201 	if (ctx->lc_source_name == NULL)
202 		return (NULL);
203 
204 	return (hd);
205 }
206 
207 /*
208  * logr_context_data_free
209  *
210  * Callback to free the context data associated with local service
211  * and/or manager context.
212  */
213 static void
214 logr_context_data_free(void *ctxp)
215 {
216 	logr_context_t *ctx = (logr_context_t *)ctxp;
217 
218 	if (ctx == NULL)
219 		return;
220 
221 	free(ctx->lc_source_name);
222 	free(ctx->lc_cached_read_data->rd_log);
223 	free(ctx->lc_cached_read_data);
224 	free(ctx);
225 	ctx = NULL;
226 }
227 
228 /*
229  * logr_hdalloc
230  *
231  * Handle allocation wrapper to setup the local manager context.
232  */
233 static ndr_hdid_t *
234 logr_hdalloc(ndr_xa_t *mxa, char *logname)
235 {
236 	logr_context_t *ctx;
237 
238 	if ((ctx = malloc(sizeof (logr_context_t))) == NULL)
239 		return (NULL);
240 	bzero(ctx, sizeof (logr_context_t));
241 
242 	ctx->lc_source_name = strdup(logname);
243 	if (ctx->lc_source_name == NULL) {
244 		free(ctx);
245 		return (NULL);
246 	}
247 
248 	if (logr_get_snapshot(ctx) != 0) {
249 		free(ctx->lc_source_name);
250 		free(ctx);
251 		return (NULL);
252 	}
253 
254 	return (ndr_hdalloc(mxa, ctx));
255 }
256 
257 /*
258  * logr_s_EventLogClose
259  *
260  * This is a request to close the LOGR interface specified by handle.
261  * Free the handle and associated resources, and zero out the result
262  * handle for the client.
263  */
264 static int
265 logr_s_EventLogClose(void *arg, ndr_xa_t *mxa)
266 {
267 	struct logr_EventLogClose *param = arg;
268 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
269 	ndr_handle_t *hd;
270 
271 	if ((hd = ndr_hdlookup(mxa, id)) == NULL) {
272 		bzero(&param->result_handle, sizeof (logr_handle_t));
273 		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
274 		return (NDR_DRC_OK);
275 	}
276 	logr_context_data_free(hd->nh_data);
277 	ndr_hdfree(mxa, id);
278 
279 	bzero(&param->result_handle, sizeof (logr_handle_t));
280 	param->status = NT_STATUS_SUCCESS;
281 
282 	return (NDR_DRC_OK);
283 }
284 
285 /*
286  * logr_s_EventLogOpen
287  *
288  * Open the event log. Not supported yet.
289  */
290 /*ARGSUSED*/
291 static int
292 logr_s_EventLogOpen(void *arg, ndr_xa_t *mxa)
293 {
294 	struct logr_EventLogOpen *param = arg;
295 	ndr_hdid_t *id = NULL;
296 	ndr_handle_t *hd;
297 	char *log_name = NULL;
298 
299 	if (!ndr_is_admin(mxa)) {
300 		bzero(&param->handle, sizeof (logr_handle_t));
301 		param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
302 		return (NDR_DRC_OK);
303 	}
304 
305 	if (param->log_name.length != 0)
306 		log_name = (char *)param->log_name.str;
307 
308 	if (!logr_is_supported(log_name)) {
309 		bzero(&param->handle, sizeof (logr_handle_t));
310 		param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
311 		return (NDR_DRC_OK);
312 	}
313 
314 	id = logr_hdalloc(mxa, log_name);
315 	if (id && ((hd = logr_hdlookup(mxa, id)) != NULL)) {
316 		hd->nh_data_free = logr_context_data_free;
317 		bcopy(id, &param->handle, sizeof (logr_handle_t));
318 		param->status = NT_STATUS_SUCCESS;
319 	} else {
320 		bzero(&param->handle, sizeof (logr_handle_t));
321 		param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED);
322 	}
323 
324 	return (NDR_DRC_OK);
325 }
326 
327 /*
328  * logr_s_EventLogQueryCount
329  *
330  * take a snapshot from system log, assign it to the given handle.
331  * return number of log entries in the snapshot as result of RPC
332  * call.
333  */
334 static int
335 logr_s_EventLogQueryCount(void *arg, ndr_xa_t *mxa)
336 {
337 	struct logr_EventLogQueryCount *param = arg;
338 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
339 	ndr_handle_t *hd;
340 	logr_context_t *ctx;
341 	logr_read_data_t *data;
342 
343 	if ((hd = logr_hdlookup(mxa, id)) == NULL) {
344 		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
345 		return (NDR_DRC_OK);
346 	}
347 
348 	ctx = (logr_context_t *)hd->nh_data;
349 	data = ctx->lc_cached_read_data;
350 
351 	param->rec_num = data->rd_tot_recnum;
352 	param->status = NT_STATUS_SUCCESS;
353 	return (NDR_DRC_OK);
354 }
355 
356 /*
357  * logr_s_EventLogGetOldestRec
358  *
359  * Return oldest record number in the snapshot as result of RPC call.
360  */
361 static int
362 logr_s_EventLogGetOldestRec(void *arg, ndr_xa_t *mxa)
363 {
364 	struct logr_EventLogGetOldestRec *param = arg;
365 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
366 	ndr_handle_t *hd;
367 	logr_context_t *ctx;
368 	logr_read_data_t *data;
369 
370 	if ((hd = logr_hdlookup(mxa, id)) == NULL) {
371 		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
372 		return (NDR_DRC_OK);
373 	}
374 
375 	ctx = (logr_context_t *)hd->nh_data;
376 	data = ctx->lc_cached_read_data;
377 
378 	param->oldest_rec = data->rd_log->li_idx - data->rd_tot_recnum + 1;
379 
380 	param->status = NT_STATUS_SUCCESS;
381 	return (NDR_DRC_OK);
382 }
383 
384 /*
385  * logr_set_event_typeid
386  *
387  * Map the local system log priority to the event type and event ID
388  * for Windows events.
389  */
390 void
391 logr_set_event_typeid(int le_pri, WORD *etype, DWORD *eid)
392 {
393 	switch (LOGR_PRI(le_pri)) {
394 	case LOG_EMERG:
395 	case LOG_ALERT:
396 	case LOG_CRIT:
397 	case LOG_ERR:
398 		*eid   = EVENTID_SEVERITY_ERROR;
399 		*etype = EVENTLOG_ERROR_TYPE;
400 		break;
401 	case LOG_WARNING:
402 		*eid   = EVENTID_SEVERITY_WARNING;
403 		*etype = EVENTLOG_WARNING_TYPE;
404 		break;
405 	case LOG_NOTICE:
406 	case LOG_INFO:
407 	case LOG_DEBUG:
408 		*eid   = EVENTID_SEVERITY_INFO;
409 		*etype = EVENTLOG_INFORMATION_TYPE;
410 		break;
411 	default:
412 		*eid   = EVENTID_SEVERITY_SUCCESS;
413 		*etype = EVENTLOG_SUCCESS;
414 	}
415 }
416 
417 /*
418  * logr_get_entry
419  *
420  * Gets a log entry.
421  */
422 static logr_entry_t *
423 logr_get_entry(logr_info_t *linfo, int entno)
424 {
425 	return (&linfo->li_entry[entno]);
426 }
427 
428 /*
429  * logr_set_logrecord
430  *
431  * Fill a Windows event record based on a local system log record.
432  */
433 static void
434 logr_set_logrecord(char *src_name, logr_entry_t *le,
435     DWORD recno, logr_record_t *rec)
436 {
437 	int srcname_len = 0, hostname_len = 0, len;
438 	int str_offs, sh_len;
439 	smb_wchar_t wcs_hostname[MAXHOSTNAMELEN];
440 	smb_wchar_t wcs_srcname[SYS_NMLN * 2];
441 
442 	(void) smb_mbstowcs(wcs_srcname, src_name,
443 	    strlen(src_name) + 1);
444 	srcname_len = LOGR_WNSTRLEN(src_name);
445 
446 	/* Because, Solaris allows remote logging, need to get hostname here */
447 	(void) smb_mbstowcs(wcs_hostname, le->le_hostname,
448 	    strlen(le->le_hostname) + 1);
449 	hostname_len = LOGR_WNSTRLEN(le->le_hostname);
450 
451 	sh_len = srcname_len + hostname_len;
452 	str_offs = LOGR_MSG_DWORD_OFFSET * sizeof (DWORD) +
453 	    LOGR_MSG_WORD_OFFSET * sizeof (WORD) + sh_len;
454 
455 	rec->Length1 = sizeof (logr_record_t);
456 	rec->Reserved = LOGR_RECORD_SIGNATURE;
457 	rec->RecordNumber = recno;
458 	rec->TimeGenerated = le->le_timestamp.tv_sec;
459 	rec->TimeWritten = le->le_timestamp.tv_sec;
460 	logr_set_event_typeid(le->le_pri, &rec->EventType, &rec->EventID);
461 	rec->NumStrings = 1;
462 	rec->EventCategory = 0;
463 	rec->ReservedFlags = 0;
464 	rec->ClosingRecordNumber = 0;
465 	rec->StringOffset = str_offs;
466 	rec->UserSidLength = 0;
467 	rec->UserSidOffset = 0;
468 	rec->DataLength = 0;
469 	rec->DataOffset = 0;
470 
471 	bzero(rec->info, LOGR_MAXENTRYLEN);
472 	(void) memcpy(rec->info, wcs_srcname, srcname_len);
473 	(void) memcpy(rec->info + srcname_len, wcs_hostname, hostname_len);
474 
475 	len = strlen(le->le_msg) + 1;
476 	if (len > 0)
477 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
478 		(void) smb_mbstowcs((smb_wchar_t *)(rec->info + sh_len),
479 		    le->le_msg, len);
480 
481 	rec->Length2 = sizeof (logr_record_t);
482 }
483 
484 /*
485  * logr_s_EventLogRead
486  *
487  * Reads a whole number of entries from system log. The function can
488  * read log entries in chronological or reverse chronological order.
489  */
490 static int
491 logr_s_EventLogRead(void *arg, ndr_xa_t *mxa)
492 {
493 	struct logr_EventLogRead *param = arg;
494 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
495 	ndr_handle_t *hd;
496 	logr_read_data_t *rdata;
497 	logr_entry_t *le;
498 	DWORD ent_no, ent_num, ent_remain;
499 	logr_record_t *rec;
500 	BYTE *buf;
501 	int dir, ent_per_req, iter;
502 	logr_context_t *ctx;
503 
504 	if ((hd = logr_hdlookup(mxa, id)) == NULL) {
505 		param->status = NT_SC_ERROR(NT_STATUS_INVALID_HANDLE);
506 		return (NDR_DRC_OK);
507 	}
508 
509 	ctx = (logr_context_t *)hd->nh_data;
510 	rdata = ctx->lc_cached_read_data;
511 	if (rdata == NULL) {
512 		param->status = NT_SC_ERROR(NT_STATUS_NO_MEMORY);
513 		return (NDR_DRC_OK);
514 	}
515 
516 	dir = (param->read_flags & EVENTLOG_FORWARDS_READ) ?
517 	    LOGR_FWD : LOGR_REW;
518 
519 	if (param->read_flags & EVENTLOG_SEEK_READ)
520 		rdata->rd_last_sentrec = param->rec_offset;
521 	else if (rdata->rd_first_read)
522 		/*
523 		 * set last record number which is read for
524 		 * the first iteration of sequential read.
525 		 */
526 		rdata->rd_last_sentrec = (dir == LOGR_FWD)
527 		    ? (rdata->rd_log->li_idx - rdata->rd_tot_recnum)
528 		    : rdata->rd_log->li_idx;
529 
530 	ent_remain = (dir == LOGR_FWD)
531 	    ? (rdata->rd_tot_recnum - rdata->rd_last_sentrec)
532 	    : rdata->rd_last_sentrec;
533 
534 	/*
535 	 * function should return as many whole log entries as
536 	 * will fit in the buffer; it should not return partial
537 	 * entries, even if there is room in the buffer.
538 	 */
539 	ent_per_req = param->nbytes_to_read / sizeof (logr_record_t);
540 	if (ent_remain > ent_per_req)
541 		ent_remain = ent_per_req;
542 
543 	if (ent_remain == 0) {
544 		/*
545 		 * Send this error to Windows client so that it
546 		 * can figure out that there is no more record
547 		 * to read.
548 		 */
549 		param->buf = NDR_STRDUP(mxa, "");
550 		param->sent_size = 0;
551 		param->status = NT_SC_ERROR(NT_STATUS_END_OF_FILE);
552 		return (NDR_DRC_OK);
553 	}
554 
555 	param->buf = NDR_MALLOC(mxa, param->nbytes_to_read);
556 	buf = (BYTE *)param->buf;
557 
558 	for (ent_num = 0, ent_no = rdata->rd_last_sentrec;
559 	    ent_num < ent_remain; ent_num++, ent_no += dir) {
560 
561 		iter = ent_no & LOGR_NMSGMASK;
562 		if (dir == LOGR_REW)
563 			iter = (ent_no - 1) & LOGR_NMSGMASK;
564 
565 		le = logr_get_entry(rdata->rd_log, iter);
566 
567 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
568 		rec = (logr_record_t *)buf;
569 		logr_set_logrecord(ctx->lc_source_name, le, ent_no, rec);
570 		buf += sizeof (logr_record_t);
571 	}
572 
573 	rdata->rd_last_sentrec = ent_no;
574 	rdata->rd_first_read = 0;
575 
576 	param->sent_size = sizeof (logr_record_t) * ent_remain;
577 	param->status = NT_STATUS_SUCCESS;
578 
579 	return (NDR_DRC_OK);
580 }
581