source: trunk/src/sh_audit.c@ 446

Last change on this file since 446 was 434, checked in by katerina, 12 years ago

Fix for ticket #339 (broken audit support)

File size: 11.4 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2010 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any 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; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22#if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
23
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <errno.h>
29#include <unistd.h>
30
31#if !defined(SH_COMPILE_STATIC) && defined(__linux__) && defined(HAVE_AUPARSE_H) && defined(HAVE_AUPARSE_LIB)
32#include <auparse.h>
33
34#include "samhain.h"
35#include "sh_error.h"
36#include "sh_extern.h"
37#include "sh_utils.h"
38
39#undef FIL__
40#define FIL__ _("sh_audit.c")
41
42#define REC_SIZE_SYSCALL 32
43#define REC_SIZE_EXE 64
44#define REC_SIZE_SUCCESS 32
45
46struct recordState {
47 char syscall[REC_SIZE_SYSCALL];
48 char exe[REC_SIZE_EXE];
49 char success[REC_SIZE_SUCCESS];
50 unsigned int auid;
51 unsigned int uid;
52 unsigned int gid;
53 unsigned int euid;
54 unsigned int egid;
55 unsigned int fsuid;
56 unsigned int fsgid;
57 time_t time;
58 unsigned int milli;
59};
60
61static int listRecords (auparse_state_t * au, struct recordState * state)
62{
63 if (auparse_first_record(au) != 1)
64 return -1;
65
66 state->time = auparse_get_time(au);
67 state->milli = auparse_get_milli(au);
68
69 if (auparse_find_field(au, _("syscall")))
70 sl_strlcpy(state->syscall, auparse_interpret_field(au), REC_SIZE_SYSCALL);
71
72 if (auparse_find_field(au, _("success")))
73 strncpy(state->success, auparse_interpret_field(au), REC_SIZE_SUCCESS);
74
75 if (auparse_find_field(au, "uid"))
76 state->uid = auparse_get_field_int(au);
77 if (auparse_find_field(au, "gid"))
78 state->gid = auparse_get_field_int(au);
79
80 if (auparse_find_field(au, _("euid")))
81 state->euid = auparse_get_field_int(au);
82 if (auparse_find_field(au, _("fsuid")))
83 state->fsuid = auparse_get_field_int(au);
84
85 auparse_first_field(au);
86
87 if (auparse_find_field(au, _("auid")))
88 state->auid = auparse_get_field_int(au);
89
90 auparse_first_field(au);
91
92 if (auparse_find_field(au, _("egid")))
93 state->egid = auparse_get_field_int(au);
94 if (auparse_find_field(au, _("fsgid")))
95 state->fsgid = auparse_get_field_int(au);
96
97 auparse_first_field(au);
98
99 if (auparse_find_field(au, "exe"))
100 sl_strlcpy(state->exe, auparse_interpret_field(au), REC_SIZE_EXE);
101
102 return 0;
103}
104
105static char * doAuparse (char * file, time_t time, char * result, size_t rsize)
106{
107 struct recordState state;
108 struct recordState stateFetched;
109
110 auparse_state_t * au = auparse_init(AUSOURCE_LOGS, NULL);
111
112 if (!au)
113 {
114 char ebuf[SH_ERRBUF_SIZE];
115 int errnum = errno;
116
117 sl_snprintf(ebuf, sizeof(ebuf), _("Error in auparse_init() - %s\n"),
118 strerror(errnum));
119 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, errnum, MSG_E_SUBGEN,
120 ebuf,
121 _("doAuparse") );
122 return NULL;
123 }
124
125 if (ausearch_add_interpreted_item(au, _("name"), "=", file,
126 AUSEARCH_RULE_CLEAR) != 0)
127 {
128 goto err;
129 }
130
131 if (time != 0)
132 {
133 ausearch_add_timestamp_item(au, ">=", time-1, 0, AUSEARCH_RULE_AND);
134 ausearch_add_timestamp_item(au, "<=", time+1, 0, AUSEARCH_RULE_AND);
135 }
136
137 if (ausearch_set_stop(au, AUSEARCH_STOP_RECORD) != 0)
138 {
139 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
140 _("Error in ausearch_set_stop\n"),
141 _("doAuparse") );
142 goto err;
143 }
144
145 memset(&state, '\0', sizeof(state));
146
147 while (ausearch_next_event(au) == 1)
148 {
149 memset(&stateFetched, '\0', sizeof(state));
150 listRecords(au, &stateFetched);
151 if (0 == strcmp(stateFetched.success, "yes"))
152 {
153 memcpy(&state, &stateFetched, sizeof(state));
154 }
155 auparse_next_event(au);
156 }
157
158 if (0 == strcmp(state.success, "yes"))
159 {
160 char * tmp_exe = sh_util_safe_name(state.exe);
161 sl_snprintf(result, rsize,
162 _("time=%lu.%u, syscall=%s, auid=%u, uid=%u, gid=%u, euid=%u, egid=%u, fsuid=%u, fsgid=%u, exe=%s"),
163 (unsigned long) state.time, state.milli,
164 state.syscall,
165 state.auid, state.uid, state.gid, state.euid, state.egid,
166 state.fsuid, state.fsgid, tmp_exe);
167 SH_FREE(tmp_exe);
168 auparse_destroy(au);
169 return result;
170 }
171
172 err:
173 auparse_destroy(au);
174 return NULL;
175}
176
177static int sh_audit_checkdaemon();
178static int actl_pnum = -1;
179static char * actl_paths[4] =
180 {
181 N_("/sbin/auditctl"),
182 N_("/usr/sbin/auditctl"),
183 N_("/bin/auditctl"),
184 N_("/usr/bin/auditctl")
185 };
186
187
188/* Public function to fetch an audit record for path 'file', time 'time'
189 * The 'result' array should be sized ~256 char.
190 */
191char * sh_audit_fetch (char * file, time_t time, char * result, size_t rsize)
192{
193 char * res = NULL;
194
195 if (sh_audit_checkdaemon() >= 0)
196 {
197 res = doAuparse (file, time, result, rsize);
198
199 if (!res)
200 {
201 res = doAuparse (file, 0, result, rsize);
202 }
203 }
204 return res;
205}
206
207void sh_audit_delete_all ()
208{
209 int p = sh_audit_checkdaemon();
210
211 if (p >= 0)
212 {
213 char ctl[64];
214
215 sl_snprintf(ctl, sizeof(ctl), _("%s -D -k samhain"),
216 _(actl_paths[p]));
217 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
218 0, MSG_E_SUBGEN,
219 _("Deleting audit daemon rules with key samhain"),
220 _("sh_audit_delete_all") );
221
222 sl_strlcpy(ctl, _(actl_paths[p]), sizeof(ctl));
223 sh_ext_system(ctl, ctl, "-D", "-k", _("samhain"), NULL);
224 }
225 return;
226}
227
228static void sh_audit_mark_int (const char * file)
229{
230 static int flushRules = 0;
231
232 int p = sh_audit_checkdaemon();
233
234 /* Flush all rules at startup.
235 */
236 if (flushRules == 0)
237 {
238 sh_audit_delete_all ();
239 flushRules = 1;
240 }
241
242 if (p >= 0)
243 {
244 size_t len = strlen(file) + 64;
245 char * command = SH_ALLOC(len);
246 char * safe;
247 char ctl[64];
248
249 sl_snprintf(command, len, _("%s -w %s -p wa -k samhain"),
250 _(actl_paths[p]),
251 file);
252
253 safe = sh_util_safe_name_keepspace(command);
254 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
255 0, MSG_E_SUBGEN,
256 safe,
257 _("sh_audit_mark") );
258 SH_FREE(safe);
259
260 sl_strlcpy(ctl, _(actl_paths[p]), sizeof(ctl));
261 sl_strlcpy(command, file, len);
262
263 sh_ext_system(ctl, ctl, "-w", command, "-p", "wa", "-k", _("samhain"), NULL);
264
265 SH_FREE(command);
266 }
267 return;
268}
269
270struct aud_list {
271 char * file;
272 struct aud_list * next;
273};
274
275struct aud_list * mark_these = NULL;
276
277static void add_this (char * file)
278{
279 struct aud_list * this = SH_ALLOC(sizeof(struct aud_list));
280 this->file = sh_util_strdup(file);
281 this->next = mark_these;
282 mark_these = this;
283 return;
284}
285
286static int test_exchange (struct aud_list * this, char * file)
287{
288 size_t len0 = sl_strlen(this->file);
289 size_t len1 = sl_strlen(file);
290 int ret = -1;
291
292 if (len0 == len1)
293 {
294 return strcmp(this->file, file);
295 }
296 else
297 {
298 char * s0 = SH_ALLOC(len0 + 2);
299 char * s1 = SH_ALLOC(len1 + 2);
300
301 sl_strlcpy(s0, this->file, len0 + 2);
302 sl_strlcpy(s1, file, len1 + 2);
303
304 if (s0 < s1)
305 {
306 sl_strlcat(s0, "/", len0 + 2);
307 ret = strncmp(s0, s1, len0 + 1);
308 }
309 else
310 {
311 sl_strlcat(s1, "/", len1 + 2);
312 if (0 == strncmp(s0, s1, len1 + 1))
313 {
314 SH_FREE(this->file);
315 this->file = sh_util_strdup(file);
316 ret = 0;
317 }
318 }
319 SH_FREE(s0);
320 SH_FREE(s1);
321 }
322
323 return ret;
324}
325
326void sh_audit_mark (char * file)
327{
328 struct aud_list * this = mark_these;
329
330 if (!mark_these) {
331 add_this (file);
332 return;
333 }
334
335 while (this)
336 {
337 if (0 == test_exchange(this, file))
338 return;
339 this = this->next;
340 }
341
342 add_this (file);
343 return;
344}
345
346void sh_audit_commit ()
347{
348 struct aud_list * next;
349 struct aud_list * this = mark_these;
350
351 mark_these = NULL;
352
353 while (this)
354 {
355 sh_audit_mark_int (this->file);
356 next = this->next;
357 SH_FREE(this->file);
358 SH_FREE(this);
359 this = next;
360 }
361
362}
363
364static int sh_audit_checkdaemon()
365{
366 int i;
367 static int flag = 0;
368 char command[64];
369 char * p;
370
371 if (flag != 0)
372 return -1;
373
374 if (actl_pnum >= 0)
375 return actl_pnum;
376
377 for (i = 0; i < 4; ++i)
378 {
379 if (0 == access(_(actl_paths[i]), F_OK))/* flawfinder: ignore */
380 {
381 if (0 == access(_(actl_paths[i]), X_OK))/* flawfinder: ignore */
382 {
383 actl_pnum = i;
384 break;
385 }
386 else
387 {
388 char ebuf[SH_ERRBUF_SIZE];
389 int errnum = errno;
390
391 sl_snprintf(ebuf, sizeof(ebuf),
392 _("Cannot execute auditctl - %s\n"),
393 strerror(errnum));
394 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
395 errnum, MSG_E_SUBGEN,
396 ebuf,
397 _("sh_audit_checkdaemon") );
398 flag = 1;
399 actl_pnum = -1;
400 return -1;
401 }
402 }
403 }
404
405 if (actl_pnum == -1 && flag == 0)
406 {
407 char ebuf[SH_ERRBUF_SIZE];
408 int errnum = errno;
409
410 sl_snprintf(ebuf, sizeof(ebuf),
411 _("Cannot find auditctl - %s\n"),
412 strerror(errnum));
413 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
414 errnum, MSG_E_SUBGEN,
415 ebuf,
416 _("sh_audit_checkdaemon") );
417 flag = 1;
418 actl_pnum = -1;
419 return -1;
420 }
421
422 /* We found an executable auditctl */
423
424 sl_snprintf(command, sizeof(command), _("%s -s"), _(actl_paths[actl_pnum]));
425 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
426 0, MSG_E_SUBGEN,
427 command,
428 _("sh_audit_checkdaemon") );
429 p = sh_ext_popen_str (command);
430
431 if (p)
432 {
433 int retval = -1;
434 if (strstr(p, _(" pid=0 ")))
435 {
436 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
437 0, MSG_E_SUBGEN,
438 _("Audit daemon for Linux kernel audit system is not running"),
439 _("sh_audit_checkdaemon") );
440 flag = 1;
441 actl_pnum = -1;
442 }
443 else
444 {
445 retval = actl_pnum;
446 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
447 retval, MSG_E_SUBGEN,
448 _("Audit daemon is running"),
449 _("sh_audit_checkdaemon") );
450 }
451 SH_FREE(p);
452 return retval;
453 }
454
455 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
456 errno, MSG_E_SUBGEN,
457 _("No output from auditctl -s"),
458 _("sh_audit_checkdaemon") );
459 flag = 1;
460 actl_pnum = -1;
461 return -1;
462}
463
464/* HAVE_AUPARSE_H */
465#else
466char * sh_audit_fetch (char * file, time_t time, char * result, size_t rsize)
467{
468 (void) file;
469 (void) time;
470 (void) result;
471 (void) rsize;
472
473 return 0;
474}
475void sh_audit_mark (const char * file)
476{
477 (void) file;
478 return;
479}
480void sh_audit_delete_all ()
481{
482 return;
483}
484void sh_audit_commit ()
485{
486 return;
487}
488#endif
489
490/* client || standalone */
491#endif
492
Note: See TracBrowser for help on using the repository browser.