Imported Upstream version 2.4.3
[platform/upstream/audit.git] / src / ausearch-report.c
1 /*
2 * ausearch-report.c - Format and output events
3 * Copyright (c) 2005-09,2011-13 Red Hat Inc., Durham, North Carolina.
4 * All Rights Reserved. 
5 *
6 * This software may be freely redistributed and/or modified under the
7 * terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING. If not, write to the
18 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Authors:
21 *   Steve Grubb <sgrubb@redhat.com>
22 */
23
24 #include "config.h"
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include "libaudit.h"
30 #include "ausearch-options.h"
31 #include "ausearch-parse.h"
32 #include "ausearch-lookup.h"
33 #include "auparse-idata.h"
34 #include "auparse-defs.h"
35
36 /* Local functions */
37 static void output_raw(llist *l);
38 static void output_default(llist *l);
39 static void output_interpreted(llist *l);
40 static void output_interpreted_node(const lnode *n, const event *e);
41 static void interpret(char *name, char *val, int comma, int rtype);
42
43 /* The machine based on elf type */
44 static unsigned long machine = -1;
45 static int cur_syscall = -1;
46
47 /* The first syscall argument */
48 static unsigned long long a0, a1;
49
50 /* This function branches to the correct output format */
51 void output_record(llist *l)
52 {
53         switch (report_format) {
54                 case RPT_RAW:
55                         output_raw(l);
56                         break;
57                 case RPT_DEFAULT:
58                         output_default(l);
59                         break;
60                 case RPT_INTERP:
61                         output_interpreted(l);
62                         break;
63                 case RPT_PRETTY:
64                         break;
65                 default:
66                         fprintf(stderr, "Report format error");
67                         exit(1);
68         }
69 }
70
71 /* This function will output the record as is */
72 static void output_raw(llist *l)
73 {
74         const lnode *n;
75
76         list_first(l);
77         n = list_get_cur(l);
78         if (!n) {
79                 fprintf(stderr, "Error - no elements in record.");
80                 return;
81         }
82         do {
83                 printf("%s\n", n->message);
84         } while ((n=list_next(l)));
85 }
86
87 /*
88  * This function will take the linked list and format it for output. No
89  * interpretation is performed. The output order is lifo for everything.
90  */
91 static void output_default(llist *l)
92 {
93         const lnode *n;
94
95         list_last(l);
96         n = list_get_cur(l);
97         printf("----\ntime->%s", ctime(&l->e.sec));
98         if (!n) {
99                 fprintf(stderr, "Error - no elements in record.");
100                 return;
101         }
102         if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) 
103                 printf("%s\n", n->message);
104         else {
105                 do {
106                         printf("%s\n", n->message);
107                 } while ((n=list_prev(l)));
108         }
109 }
110
111 /*
112  * This function will take the linked list and format it for output. 
113  * Interpretation is performed to aid understanding of records. The output
114  * order is lifo for everything.
115  */
116 static void output_interpreted(llist *l)
117 {
118         const lnode *n;
119
120         list_last(l);
121         n = list_get_cur(l);
122         printf("----\n");
123         if (!n) {
124                 fprintf(stderr, "Error - no elements in record.");
125                 return;
126         }
127         if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) 
128                 output_interpreted_node(n, &(l->e));
129         else {
130                 do {
131                         output_interpreted_node(n, &(l->e));
132                 } while ((n=list_prev(l)));
133         }
134 }
135
136 /*
137  * This function will cycle through a single record and lookup each field's
138  * value that it finds. 
139  */
140 static void output_interpreted_node(const lnode *n, const event *e)
141 {
142         char *ptr, *str = n->message;
143         int found, comma = 0;
144         int num = n->type;
145         struct tm *btm;
146         char tmp[32];
147
148         // Reset these because each record could be different
149         machine = -1;
150         cur_syscall = -1;
151
152         /* Check and see if we start with a node
153          * If we do, and there is a space in the line
154          * move the pointer to the first character past
155          * the space
156          */
157         if (e->node) {
158                 if ((ptr=strchr(str, ' ')) != NULL) {
159                         str = ptr+1;
160                 }
161         }
162
163         // First locate time stamp.
164         ptr = strchr(str, '(');
165         if (ptr == NULL) {
166                 fprintf(stderr, "can't find time stamp\n");
167                 return;
168         }
169
170         *ptr++ = 0;     /* move to the start of the timestamp */
171
172         // print everything up to it.
173         if (num >= 0) {
174                 const char      * bptr;
175                 bptr = audit_msg_type_to_name(num);
176                 if (bptr) {
177                         if (e->node)
178                                 printf("node=%s ", e->node);
179                         printf("type=%s msg=audit(", bptr);
180                         goto no_print;
181                 }
182         } 
183         if (e->node)
184                 printf("node=%s ", e->node);
185         printf("%s(", str);
186 no_print:
187
188         str = strchr(ptr, ')');
189         if(str == NULL)
190                 return;
191         *str++ = 0;
192         btm = localtime(&e->sec);
193         strftime(tmp, sizeof(tmp), "%x %T", btm);
194         printf("%s", tmp);
195         printf(".%03d:%lu) ", e->milli, e->serial);
196
197         if (n->type == AUDIT_SYSCALL) { 
198                 a0 = n->a0;
199                 a1 = n->a1;
200         }
201
202         // for each item.
203         found = 0;
204         while (str && *str && (ptr = strchr(str, '='))) {
205                 char *name, *val;
206                 comma = 0;
207                 found = 1;
208
209                 // look back to last space - this is name
210                 name = ptr;
211                 while (*name != ' ' && name > str)
212                         --name;
213                 *ptr++ = 0;
214
215                 // print everything up to the '='
216                 printf("%s=", str);
217
218                 // Some user messages have msg='uid=500   in this case
219                 // skip the msg= piece since the real stuff is the uid=
220                 if (strcmp(name, "msg") == 0) {
221                         str = ptr;
222                         continue;
223                 }
224
225                 // In the above case, after msg= we need to trim the ' from uid
226                 if (*name == '\'')
227                         name++;
228
229                 // get string after = to the next space or end - this is value
230                 if (*ptr == '\'' || *ptr == '"') {
231                         str = strchr(ptr+1, *ptr);
232                         if (str) {
233                                 str++;
234                                 if (*str)
235                                         *str++ = 0;
236                         }
237                 } else {
238                         str = strchr(ptr, ',');
239                         val = strchr(ptr, ' ');
240                         if (str && val && (str < val)) {
241                         // Value side  has commas and another field exists
242                         // Known: LABEL_LEVEL_CHANGE banners=none,none
243                         // Known: ROLL_ASSIGN new-role=r,r
244                         // Known: any MAC LABEL can potentially have commas
245                                 int ftype = auparse_interp_adjust_type(n->type,
246                                                                 name, val);
247                                 if (ftype == AUPARSE_TYPE_MAC_LABEL) {
248                                         str = val;
249                                         *str++ = 0;
250                                 } else {
251                                         *str++ = 0;
252                                         comma = 1;
253                                 }
254                         } else if (str && (val == NULL)) {
255                         // Goes all the way to the end. Done parsing
256                         // Known: MCS context in PATH rec obj=u:r:t:s0:c2,c7
257                                 int ftype = auparse_interp_adjust_type(n->type,
258                                                                 name, ptr);
259                                 if (ftype == AUPARSE_TYPE_MAC_LABEL)
260                                         str = NULL;
261                                 else {
262                                         *str++ = 0;
263                                         comma = 1;
264                                 }
265                         } else if (val) {
266                         // There is another field, point to next (normal path)
267                                 str = val;
268                                 *str++ = 0;
269                         }
270                 }
271                 // val points to begin & str 1 past end
272                 val = ptr;
273                 
274                 // print interpreted string
275                 interpret(name, val, comma, n->type);
276         }
277         // If nothing found, just print out as is
278         if (!found && ptr == NULL && str)
279                 printf("%s", str);
280         // If last field had comma, output the rest
281         else if (comma)
282                 printf("%s", str);
283         printf("\n");
284 }
285
286 static void interpret(char *name, char *val, int comma, int rtype)
287 {
288         int type;
289         idata id;
290
291         while (*name == ' '||*name == '(')
292                 name++;
293
294         if (*name == 'a' && strcmp(name, "acct") == 0) {
295                 // Remove trailing punctuation
296                 int len = strlen(val);
297                 if (val[len-1] == ':')
298                         val[len-1] = 0;
299         }
300         type = auparse_interp_adjust_type(rtype, name, val);
301
302         if (rtype == AUDIT_SYSCALL || rtype == AUDIT_SECCOMP) {
303                 if (machine == (unsigned long)-1) 
304                         machine = audit_detect_machine();
305                 if (*name == 'a' && strcmp(name, "arch") == 0) {
306                         unsigned long ival;
307                         errno = 0;
308                         ival = strtoul(val, NULL, 16);
309                         if (errno) {
310                                 printf("arch conversion error(%s) ", val);
311                                 return;
312                         }
313                         machine = audit_elf_to_machine(ival);
314                 }
315                 if (cur_syscall < 0 && *name == 's' &&
316                                 strcmp(name, "syscall") == 0) {
317                         unsigned long ival;
318                         errno = 0;
319                         ival = strtoul(val, NULL, 10);
320                         if (errno) {
321                                 printf("syscall conversion error(%s) ", val);
322                                 return;
323                         }
324                         cur_syscall = ival;
325                 }
326                 id.syscall = cur_syscall;
327         } else
328                 id.syscall = 0;
329         id.machine = machine;
330         id.a0 = a0;
331         id.a1 = a1;
332         id.name = name;
333         id.val = val;
334
335         char *out = auparse_do_interpretation(type, &id);
336         if (type == AUPARSE_TYPE_UNCLASSIFIED)
337                 printf("%s%c", val, comma ? ',' : ' ');
338         else if (name[0] == 'k' && strcmp(name, "key") == 0) {
339                 char *str, *ptr = out;
340                 int count = 0;
341                 while ((str = strchr(ptr, AUDIT_KEY_SEPARATOR))) {
342                         *str = 0;
343                         if (count == 0) {
344                                 printf("%s", ptr);
345                                 count++;
346                         } else
347                                 printf(" key=%s", ptr);
348                         ptr = str+1;
349                 }
350                 if (count == 0)
351                         printf("%s ", out);
352                 else
353                         printf(" key=%s ", ptr);
354         } else if (type == AUPARSE_TYPE_TTY_DATA)
355                 printf("%s", out);
356         else
357                 printf("%s ", out);
358         free(out);
359 }
360