1 /* mmaudit.c
2 * This is a message modification module supporting Linux audit format
3 * in various settings. The module tries to identify the provided
4 * message as being a Linux audit record and, if so, converts it into
5 * cee-enhanced syslog format.
6 *
7 * NOTE WELL:
8 * Right now, we do not do any trust checks. So it is possible that a
9 * malicous user emits something that looks like an audit record and
10 * tries to fool the system with that. Solving this trust issue is NOT
11 * an easy thing to do. This will be worked on, as the lumberjack effort
12 * continues. Please consider the module in its current state as a proof
13 * of concept.
14 *
15 * File begun on 2012-02-23 by RGerhards
16 *
17 * Copyright 2013-2016 Adiscon GmbH.
18 *
19 * This file is part of rsyslog.
20 *
21 * Licensed under the Apache License, Version 2.0 (the "License");
22 * you may not use this file except in compliance with the License.
23 * You may obtain a copy of the License at
24 *
25 * http://www.apache.org/licenses/LICENSE-2.0
26 * -or-
27 * see COPYING.ASL20 in the source distribution
28 *
29 * Unless required by applicable law or agreed to in writing, software
30 * distributed under the License is distributed on an "AS IS" BASIS,
31 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32 * See the License for the specific language governing permissions and
33 * limitations under the License.
34 */
35 #include "config.h"
36 #include "rsyslog.h"
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <assert.h>
42 #include <signal.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <ctype.h>
46 #include <json.h>
47 #include "conf.h"
48 #include "syslogd-types.h"
49 #include "template.h"
50 #include "module-template.h"
51 #include "errmsg.h"
52 #include "cfsysline.h"
53 #include "dirty.h"
54
55 MODULE_TYPE_OUTPUT
56 MODULE_TYPE_NOKEEP
57
58
59 /* static data */
60
61 /* internal structures
62 */
63 DEF_OMOD_STATIC_DATA
64
65 typedef struct _instanceData {
66 int dummy; /* remove when the first real parameter is needed */
67 } instanceData;
68
69 typedef struct wrkrInstanceData {
70 instanceData *pData;
71 } wrkrInstanceData_t;
72
73
74 BEGINinitConfVars /* (re)set config variables to default values */
75 CODESTARTinitConfVars
76 ENDinitConfVars
77
78
79 BEGINcreateInstance
80 CODESTARTcreateInstance
81 ENDcreateInstance
82
83 BEGINcreateWrkrInstance
84 CODESTARTcreateWrkrInstance
85 ENDcreateWrkrInstance
86
87
88 BEGINisCompatibleWithFeature
89 CODESTARTisCompatibleWithFeature
90 ENDisCompatibleWithFeature
91
92
93 BEGINfreeInstance
94 CODESTARTfreeInstance
95 ENDfreeInstance
96
97 BEGINfreeWrkrInstance
98 CODESTARTfreeWrkrInstance
99 ENDfreeWrkrInstance
100
101
102 BEGINdbgPrintInstInfo
103 CODESTARTdbgPrintInstInfo
104 dbgprintf("mmaudit\n");
105 ENDdbgPrintInstInfo
106
107
108 BEGINtryResume
109 CODESTARTtryResume
110 ENDtryResume
111
112
113 static void
skipWhitespace(uchar ** buf)114 skipWhitespace(uchar **buf)
115 {
116 while(**buf && isspace(**buf))
117 ++(*buf);
118 }
119
120
121 static rsRetVal
parseName(uchar ** buf,char * name,unsigned lenName)122 parseName(uchar **buf, char *name, unsigned lenName)
123 {
124 unsigned i;
125 skipWhitespace(buf);
126 --lenName; /* reserve space for '\0' */
127 i = 0;
128 while(**buf && **buf != '=' && lenName) {
129 name[i++] = **buf;
130 ++(*buf), --lenName;
131 }
132 name[i] = '\0';
133 return RS_RET_OK;
134 }
135
136
137 static rsRetVal
parseValue(uchar ** buf,char * val,unsigned lenval)138 parseValue(uchar **buf, char *val, unsigned lenval)
139 {
140 char termc;
141 unsigned i;
142 DEFiRet;
143
144 --lenval; /* reserve space for '\0' */
145 i = 0;
146 if(**buf == '\0') {
147 FINALIZE;
148 } else if(**buf == '\'') {
149 termc = '\'';
150 ++(*buf);
151 } else if(**buf == '"') {
152 termc = '"';
153 ++(*buf);
154 } else {
155 termc = ' ';
156 }
157
158 while(**buf && **buf != termc && lenval) {
159 val[i++] = **buf;
160 ++(*buf), --lenval;
161 }
162 val[i] = '\0';
163
164 finalize_it:
165 RETiRet;
166 }
167
168
169 /* parse the audit record and create json structure
170 */
171 static rsRetVal
audit_parse(uchar * buf,struct json_object ** jsonRoot)172 audit_parse(uchar *buf, struct json_object **jsonRoot)
173 {
174 struct json_object *json;
175 struct json_object *jval;
176 char name[1024];
177 char val[1024];
178 DEFiRet;
179
180 *jsonRoot = json_object_new_object();
181 if(*jsonRoot == NULL) {
182 ABORT_FINALIZE(RS_RET_ERR);
183 }
184 json = json_object_new_object();
185 json_object_object_add(*jsonRoot, "data", json);
186
187 while(*buf) {
188 CHKiRet(parseName(&buf, name, sizeof(name)));
189 if(*buf != '=') {
190 ABORT_FINALIZE(RS_RET_ERR);
191 }
192 ++buf;
193 CHKiRet(parseValue(&buf, val, sizeof(val)));
194 jval = json_object_new_string(val);
195 json_object_object_add(json, name, jval);
196 }
197
198
199 finalize_it:
200 RETiRet;
201 }
202
203
204 BEGINdoAction_NoStrings
205 smsg_t **ppMsg = (smsg_t **) pMsgData;
206 smsg_t *pMsg = ppMsg[0];
207 uchar *buf;
208 int typeID;
209 struct json_object *jsonRoot;
210 struct json_object *json;
211 struct json_object *jval;
212 int i;
213 char auditID[1024];
214 int bSuccess = 0;
215 CODESTARTdoAction
216 /* note that we can performance-optimize the interface, but this also
217 * requires changes to the libraries. For now, we accept message
218 * duplication. -- rgerhards, 2010-12-01
219 */
220 buf = getMSG(pMsg);
221
222 while(*buf && isspace(*buf)) {
223 ++buf;
224 }
225
226 if(*buf == '\0' || strncmp((char*)buf, "type=", 5)) {
227 DBGPRINTF("mmaudit: type= undetected: '%s'\n", buf);
228 FINALIZE;
229 }
230 buf += 5;
231
232 typeID = 0;
233 while(*buf && isdigit(*buf)) {
234 typeID = typeID * 10 + *buf - '0';
235 ++buf;
236 }
237
238 if(*buf == '\0' || strncmp((char*)buf, " audit(", sizeof(" audit(")-1)) {
239 DBGPRINTF("mmaudit: audit( header not found: %s'\n", buf);
240 FINALIZE;
241 }
242 buf += sizeof(" audit(");
243
244 for(i = 0 ; i < (int) (sizeof(auditID)-2) && *buf && *buf != ')' ; ++i) {
245 auditID[i] = *buf++;
246 }
247 auditID[i] = '\0';
248 if(*buf != ')' || *(buf+1) != ':') {
249 DBGPRINTF("mmaudit: trailer '):' not found, no audit record: %s'\n", buf);
250 FINALIZE;
251 }
252 buf += 2;
253
254 audit_parse(buf, &jsonRoot);
255 if(jsonRoot == NULL) {
256 DBGPRINTF("mmaudit: audit parse error, assuming no "
257 "audit message: '%s'\n", buf);
258 FINALIZE;
259 }
260
261 /* we now need to shuffle the "outer" properties into that stream */
262 json = json_object_new_object();
263 json_object_object_add(jsonRoot, "hdr", json);
264 jval = json_object_new_string(auditID);
265 json_object_object_add(json, "auditid", jval);
266 jval = json_object_new_int(typeID);
267 json_object_object_add(json, "type", jval);
268
269 msgAddJSON(pMsg, (uchar*)"!audit", jsonRoot, 0, 0);
270 bSuccess = 1;
271
272 finalize_it:
273 MsgSetParseSuccess(pMsg, bSuccess);
274 ENDdoAction
275
276
277 BEGINparseSelectorAct
278 CODESTARTparseSelectorAct
279 CODE_STD_STRING_REQUESTparseSelectorAct(1)
280 /* first check if this config line is actually for us */
281 if(strncmp((char*) p, ":mmaudit:", sizeof(":mmaudit:") - 1)) {
282 ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
283 }
284
285 /* ok, if we reach this point, we have something for us */
286 p += sizeof(":mmaudit:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
287 CHKiRet(createInstance(&pData));
288
289 /* check if a non-standard template is to be applied */
290 if(*(p-1) == ';')
291 --p;
292 /* we call the function below because we need to call it via our interface definition. However,
293 * the format specified (if any) is always ignored.
294 */
295 iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat");
296 CODE_STD_FINALIZERparseSelectorAct
297 ENDparseSelectorAct
298
299
300 BEGINmodExit
301 CODESTARTmodExit
302 ENDmodExit
303
304
305 BEGINqueryEtryPt
306 CODESTARTqueryEtryPt
307 CODEqueryEtryPt_STD_OMOD_QUERIES
308 CODEqueryEtryPt_STD_OMOD8_QUERIES
309 ENDqueryEtryPt
310
311
312 BEGINmodInit()
313 rsRetVal localRet;
314 rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
315 unsigned long opts;
316 int bMsgPassingSupported;
317 CODESTARTmodInit
318 INITLegCnfVars
319 *ipIFVersProvided = CURR_MOD_IF_VERSION;
320 /* we only support the current interface specification */
321 CODEmodInit_QueryRegCFSLineHdlr
322 /* check if the rsyslog core supports parameter passing code */
323 bMsgPassingSupported = 0;
324 localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts",
325 &pomsrGetSupportedTplOpts);
326 if(localRet == RS_RET_OK) {
327 /* found entry point, so let's see if core supports msg passing */
328 CHKiRet((*pomsrGetSupportedTplOpts)(&opts));
329 if(opts & OMSR_TPL_AS_MSG)
330 bMsgPassingSupported = 1;
331 } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) {
332 ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */
333 }
334
335 if(!bMsgPassingSupported) {
336 DBGPRINTF("mmaudit: msg-passing is not supported by rsyslog core, "
337 "can not continue.\n");
338 ABORT_FINALIZE(RS_RET_NO_MSG_PASSING);
339 }
340
341 ENDmodInit
342
343 /* vi:set ai:
344 */
345