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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <dirent.h>
30#include <locale.h>
31#include <libintl.h>
32#include <stdlib.h>
33#include <strings.h>
34#include <stdio.h>
35#include <unistd.h>
36
37#include <sys/types.h>
38#include <sys/file.h>
39
40#include <bsm/audit.h>
41#include <bsm/audit_record.h>
42#include <bsm/libbsm.h>
43
44#include "praudit.h"
45#include "toktable.h"
46
47extern void	init_tokens(void);	/* shared with auditreduce */
48
49static int	check_inputs(int flags, const char *separator);
50static void	checkpoint_progress(pr_context_t *context);
51static int	print_audit_common(pr_context_t *context, int flags,
52    const char *separator);
53static int	token_processing(pr_context_t *context);
54
55static int	initdone = 0;
56
57/*
58 * This source is shared outside of praudit; the following lint directive
59 * is needed to suppress praudit lint warnings about unused functions, for
60 * functions which are only invoked outside praudit.
61 */
62
63/*LINTLIBRARY*/
64
65/*
66 * ----------------------------------------------------------------------
67 * check_inputs() - check input flags and delimiter.
68 *		Returns:
69 *		    0 - successful
70 *		   -1 - invalid inputs. errno is set to EINVAL
71 * ----------------------------------------------------------------------
72 */
73static int
74check_inputs(int flags, const char *separator)
75{
76	if ((flags & PRF_RAWM) && (flags & PRF_SHORTM)) {
77		errno = EINVAL;
78		return (-1);
79	}
80
81	/* Ignore the delimiter when XML is specified */
82	if (!(flags & PRF_XMLM) && (strlen(separator) >= SEP_SIZE)) {
83		errno = EINVAL;
84		return (-1);
85	}
86
87	return (0);
88}
89
90/*
91 * ----------------------------------------------------------------------
92 * print_audit_xml_prolog_buf() - print the XML prolog.
93 *		    0 - successful
94 *		   -1 - output buffer too small. errno is set to ENOSPC
95 * ----------------------------------------------------------------------
96 */
97int
98print_audit_xml_prolog_buf(char *out_buf, const int out_buf_len)
99{
100	if (xml_prolog_len > out_buf_len) {
101		errno = ENOSPC;
102		return (-1);
103	}
104
105	(void) snprintf(out_buf, out_buf_len, "%s%s%s%s", prolog1, prolog_xsl,
106	    prolog2, xml_start);
107
108	return (0);
109}
110
111/*
112 * ----------------------------------------------------------------------
113 * print_audit_xml_ending_buf() - print the XML ending.
114 *		    0 - successful
115 *		   -1 - output buffer too small. errno is set to ENOSPC
116 * ----------------------------------------------------------------------
117 */
118int
119print_audit_xml_ending_buf(char *out_buf, const int out_buf_len)
120{
121	if (xml_end_len > out_buf_len) {
122		errno = ENOSPC;
123		return (-1);
124	}
125
126	(void) snprintf(out_buf, out_buf_len, "%s", xml_ending);
127	return (0);
128}
129
130/*
131 * ----------------------------------------------------------------------
132 * print_prolog() - print the XML prolog.
133 * ----------------------------------------------------------------------
134 */
135void
136print_audit_xml_prolog(void)
137{
138	(void) printf("%s%s%s%s", prolog1, prolog_xsl, prolog2, xml_start);
139}
140
141/*
142 * ----------------------------------------------------------------------
143 * print_ending() - print the XML ending.
144 * ----------------------------------------------------------------------
145 */
146void
147print_audit_xml_ending(void)
148{
149	(void) printf("%s", xml_ending);
150}
151
152/*
153 * ----------------------------------------------------------------------
154 * checkpoint_progress() - If starting a new file or header token,
155 *      checkpoint as needed to mark progress.
156 * ----------------------------------------------------------------------
157 */
158static void
159checkpoint_progress(pr_context_t *context)
160{
161	int	tokenid = context->tokenid;
162
163	if (is_file_token(tokenid) || is_header_token(tokenid)) {
164		if (context->data_mode == BUFMODE) {
165			context->inbuf_last = context->audit_adr->adr_now - 1;
166			context->outbuf_last = context->outbuf_p;
167		}
168		context->audit_rec_start = context->audit_adr->adr_now - 1;
169		if (is_file_token(tokenid)) {
170			context->audit_rec_len = 11;
171		}
172	}
173}
174
175/*
176 * ----------------------------------------------------------------------
177 * print_audit_buf() - display contents of audit trail file
178 *
179 *		   Parses the binary audit data from the specified input
180 *		   buffer, and formats as requested to the specified output
181 *		   buffer.
182 *
183 *	inputs:
184 *		   in_buf, -	address and length of binary audit input.
185 *		   in_buf_len
186 *		   out_buf, -	address and length of output buffer to
187 *		   out_buf_len	copy formatted audit data to.
188 *		   flags -	formatting flags as defined in praudit.h
189 *		   separator -	field delimiter (or NULL if the default
190 *				delimiter of comma is to be used).
191 *
192 * return codes:    0 - success
193 *		ENOSPC...
194 * ----------------------------------------------------------------------
195 */
196int
197print_audit_buf(char **in_buf, int *in_buf_len, char **out_buf,
198    int *out_buf_len, const int flags, const char *separator)
199{
200	int	retstat = 0;
201	pr_context_t	*context;
202
203	if ((retstat = check_inputs(flags, separator)) != 0)
204		return (retstat);
205
206	if ((context = (pr_context_t *)malloc(sizeof (pr_context_t))) == NULL) {
207		errno = EPERM;
208		return (-1);
209	}
210
211	/* Init internal pointers and lengths... */
212	context->data_mode = BUFMODE;
213	context->inbuf_last = context->inbuf_start = *in_buf;
214	context->inbuf_totalsize = *in_buf_len;
215
216	context->pending_flag = 0;
217	context->current_rec = 0;
218
219	context->outbuf_last = context->outbuf_start =
220	    context->outbuf_p = *out_buf;
221	context->outbuf_remain_len = *out_buf_len;
222
223	/*
224	 * get an adr pointer to the audit input buf
225	 */
226	context->audit_adr = (adr_t *)malloc(sizeof (adr_t));
227	(void) adrm_start(context->audit_adr, *in_buf);
228	context->audit_rec_start = NULL;
229	context->audit_rec_len = 0;
230
231	retstat = print_audit_common(context, flags, separator);
232
233	/* Check for and handle partial results as needed */
234	if (retstat != 0) {
235		*in_buf = context->inbuf_last;
236		*in_buf_len = context->inbuf_totalsize -
237		    (context->inbuf_last - context->inbuf_start);
238
239		/* Return size of output */
240		*out_buf_len = context->outbuf_last - context->outbuf_start;
241		if (*out_buf_len > 0) {
242			/* null-terminate the output */
243			*(context->outbuf_last) = '\0';
244			*out_buf_len = *out_buf_len + 1;
245		}
246	} else {
247		/* Return size of output */
248		*out_buf_len = context->outbuf_p - context->outbuf_start + 1;
249		*(context->outbuf_p) = '\0';	/* null-terminate the output */
250	}
251
252	(void) free(context->audit_adr);
253	(void) free(context);
254	return (retstat);
255}
256
257/*
258 * ----------------------------------------------------------------------
259 * print_audit() - display contents of audit trail file
260 *
261 *		   Parses the binary audit data from the file mapped as stdin,
262 *		   and formats as requested to file mapped as stdout.
263 *	inputs:
264 *		   flags -	formatting flags as defined in praudit.h
265 *		   separator -	field delimiter (or NULL if the default
266 *				delimiter of comma is to be used).
267 *
268 * return codes:   -1 - error
269 *		    0 - successful
270 * ----------------------------------------------------------------------
271 */
272int
273print_audit(const int flags, const char *separator)
274{
275	int	retstat = 0;
276	pr_context_t	*context;
277
278	if ((retstat = check_inputs(flags, separator)) != 0)
279		return (retstat);
280
281	if ((context = (pr_context_t *)malloc(sizeof (pr_context_t))) == NULL) {
282		errno = EPERM;
283		return (-1);
284	}
285
286	/*
287	 * get an adr pointer to the current audit file (stdin)
288	 */
289	context->audit_adr = malloc(sizeof (adr_t));
290	context->audit_adrf = malloc(sizeof (adrf_t));
291
292	adrf_start(context->audit_adrf, context->audit_adr, stdin);
293
294	context->data_mode = FILEMODE;
295	context->audit_rec_start = NULL;
296	context->audit_rec_len = 0;
297
298	context->pending_flag = 0;
299	context->current_rec = 0;
300
301	retstat = print_audit_common(context, flags, separator);
302
303	(void) free(context->audit_adr);
304	(void) free(context->audit_adrf);
305	(void) free(context);
306	return (retstat);
307}
308
309/*
310 * ----------------------------------------------------------------------
311 * print_audit_common() - common routine for print_audit* functions.
312 *
313 *		   Parses the binary audit data, and formats as requested.
314 *		   The context parameter defines whether the source of the
315 *		   audit data is a buffer, or a file mapped to stdin, and
316 *		   whether the output is to a buffer or a file mapped to
317 *		   stdout.
318 *
319 *	inputs:
320 *		   context -	defines the context of the request, including
321 *				info about the source and output.
322 *		   flags -	formatting flags as defined in praudit.h
323 *		   separator -	field delimiter (or NULL if the default
324 *				delimiter of comma is to be used).
325 *
326 * return codes:   -1 - error
327 *		    0 - successful
328 * ----------------------------------------------------------------------
329 */
330static int
331print_audit_common(pr_context_t *context, const int flags,
332    const char *separator)
333{
334	int	retstat = 0;
335
336	if (!initdone) {
337		init_tokens();
338		initdone++;
339	}
340
341	context->format = flags;
342
343	/* start with default delimiter of comma */
344	(void) strlcpy(context->SEPARATOR, ",", SEP_SIZE);
345	if (separator != NULL) {
346		if (strlen(separator) < SEP_SIZE) {
347			(void) strlcpy(context->SEPARATOR, separator, SEP_SIZE);
348		}
349	}
350
351	while ((retstat == 0) && pr_input_remaining(context, 1)) {
352		if (pr_adr_char(context, (char *)&(context->tokenid), 1) == 0) {
353			retstat = token_processing(context);
354		} else
355			break;
356	}
357
358	/*
359	 * For buffer processing, if the entire input buffer was processed
360	 * successfully, but the last record in the buffer was incomplete
361	 * (according to the length from its header), then reflect an
362	 * "incomplete input" error (which will cause partial results to be
363	 * returned).
364	 */
365	if ((context->data_mode == BUFMODE) && (retstat == 0) &&
366	    (context->audit_adr->adr_now < (context->audit_rec_start +
367	    context->audit_rec_len))) {
368		retstat = -1;
369		errno = EIO;
370	}
371
372	/*
373	 * If there was a last record that didn't get officially closed
374	 * off, do it now.
375	 */
376	if ((retstat == 0) && (context->format & PRF_XMLM) &&
377	    (context->current_rec)) {
378		retstat = do_newline(context, 1);
379		if (retstat == 0)
380			retstat = close_tag(context, context->current_rec);
381	}
382
383	return (retstat);
384}
385
386/*
387 * -----------------------------------------------------------------------
388 * token_processing:
389 *		  Calls the routine corresponding to the token id
390 *		  passed in the parameter from the token table, tokentable
391 * return codes : -1 - error
392 *		:  0 - successful
393 * -----------------------------------------------------------------------
394 */
395static int
396token_processing(pr_context_t *context)
397{
398	uval_t	uval;
399	int	retstat;
400	int	tokenid = context->tokenid;
401
402	if ((tokenid > 0) && (tokenid <= MAXTOKEN) &&
403	    (tokentable[tokenid].func != NOFUNC)) {
404		/*
405		 * First check if there's a previous record that needs to be
406		 * closed off now; then checkpoint our progress as needed.
407		 */
408		if ((retstat = check_close_rec(context, tokenid)) != 0)
409			return (retstat);
410		checkpoint_progress(context);
411
412		/* print token name */
413		if (context->format & PRF_XMLM) {
414			retstat = open_tag(context, tokenid);
415		} else {
416			if (!(context->format & PRF_RAWM) &&
417			    (tokentable[tokenid].t_name != (char *)0)) {
418				uval.uvaltype = PRA_STRING;
419				uval.string_val =
420				    gettext(tokentable[tokenid].t_name);
421			} else {
422				uval.uvaltype = PRA_BYTE;
423				uval.char_val = tokenid;
424			}
425			retstat = pa_print(context, &uval, 0);
426		}
427		if (retstat == 0)
428			retstat = (*tokentable[tokenid].func)(context);
429
430		/*
431		 * For XML, close the token tag. Header tokens wrap the
432		 * entire record, so they only get closed later implicitly;
433		 * here, just make sure the header open tag gets finished.
434		 */
435		if ((retstat == 0) && (context->format & PRF_XMLM)) {
436			if (!is_header_token(tokenid))
437				retstat = close_tag(context, tokenid);
438			else
439				retstat = finish_open_tag(context);
440		}
441		return (retstat);
442	}
443	/* here if token id is not in table */
444	(void) fprintf(stderr, gettext("praudit: No code associated with "
445	    "token id %d\n"), tokenid);
446	return (0);
447}
448