source: trunk/src/sh_audit.c@ 488

Last change on this file since 488 was 488, checked in by katerina, 9 years ago

Fix for tickets #386 (silent check) and #387 (linux audit support).

File size: 13.5 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#include <fcntl.h>
31#include <sys/stat.h>
32
33#include "samhain.h"
34#include "sh_error.h"
35
36#if !defined(SH_COMPILE_STATIC) && defined(__linux__) && defined(HAVE_AUPARSE_H) && defined(HAVE_AUPARSE_LIB)
37#include <auparse.h>
38
39#include "sh_extern.h"
40#include "sh_utils.h"
41
42#undef FIL__
43#define FIL__ _("sh_audit.c")
44
45#define REC_SIZE_SYSCALL 32
46#define REC_SIZE_EXE 64
47#define REC_SIZE_SUCCESS 32
48
49struct recordState {
50 char syscall[REC_SIZE_SYSCALL];
51 char exe[REC_SIZE_EXE];
52 char success[REC_SIZE_SUCCESS];
53 unsigned int auid;
54 unsigned int uid;
55 unsigned int gid;
56 unsigned int euid;
57 unsigned int egid;
58 unsigned int fsuid;
59 unsigned int fsgid;
60 time_t time;
61 unsigned int milli;
62};
63
64static int listRecords (auparse_state_t * au, struct recordState * state)
65{
66 if (auparse_first_record(au) != 1)
67 return -1;
68
69 state->time = auparse_get_time(au);
70 state->milli = auparse_get_milli(au);
71
72 if (auparse_find_field(au, _("syscall")))
73 sl_strlcpy(state->syscall, auparse_interpret_field(au), REC_SIZE_SYSCALL);
74
75 if (auparse_find_field(au, _("success")))
76 strncpy(state->success, auparse_interpret_field(au), REC_SIZE_SUCCESS);
77
78 if (auparse_find_field(au, "uid"))
79 state->uid = auparse_get_field_int(au);
80 if (auparse_find_field(au, "gid"))
81 state->gid = auparse_get_field_int(au);
82
83 if (auparse_find_field(au, _("euid")))
84 state->euid = auparse_get_field_int(au);
85 if (auparse_find_field(au, _("fsuid")))
86 state->fsuid = auparse_get_field_int(au);
87
88 auparse_first_field(au);
89
90 if (auparse_find_field(au, _("auid")))
91 state->auid = auparse_get_field_int(au);
92
93 auparse_first_field(au);
94
95 if (auparse_find_field(au, _("egid")))
96 state->egid = auparse_get_field_int(au);
97 if (auparse_find_field(au, _("fsgid")))
98 state->fsgid = auparse_get_field_int(au);
99
100 auparse_first_field(au);
101
102 if (auparse_find_field(au, "exe"))
103 sl_strlcpy(state->exe, auparse_interpret_field(au), REC_SIZE_EXE);
104
105 return 0;
106}
107
108static char * doAuparse (const char * file, time_t time, int tol, char * result, size_t rsize, int redo_flag)
109{
110 struct recordState state;
111 struct recordState stateFetched;
112 unsigned int found_flag = 0;
113
114 auparse_state_t * au = auparse_init(AUSOURCE_LOGS, NULL);
115
116 if (!au)
117 {
118 char ebuf[SH_ERRBUF_SIZE];
119 int errnum = errno;
120
121 sl_snprintf(ebuf, sizeof(ebuf), _("Error in auparse_init() - %s\n"),
122 strerror(errnum));
123 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, errnum, MSG_E_SUBGEN,
124 ebuf,
125 _("doAuparse") );
126 return NULL;
127 }
128
129 if (ausearch_add_interpreted_item(au, _("name"), "=", file,
130 AUSEARCH_RULE_CLEAR) != 0)
131 {
132 goto err;
133 }
134
135 if (time != 0)
136 {
137 ausearch_add_timestamp_item(au, ">=", time-tol, 0, AUSEARCH_RULE_AND);
138 ausearch_add_timestamp_item(au, "<=", time+tol, 0, AUSEARCH_RULE_AND);
139 }
140
141 if (ausearch_set_stop(au, AUSEARCH_STOP_RECORD) != 0)
142 {
143 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
144 _("Error in ausearch_set_stop\n"),
145 _("doAuparse") );
146 goto err;
147 }
148
149 memset(&state, '\0', sizeof(state));
150
151 while (ausearch_next_event(au) == 1)
152 {
153 memset(&stateFetched, '\0', sizeof(state));
154 listRecords(au, &stateFetched);
155 if (0 == strcmp(stateFetched.success, "yes"))
156 {
157 memcpy(&state, &stateFetched, sizeof(state));
158 ++found_flag;
159 }
160 auparse_next_event(au);
161 }
162
163 if (found_flag == 0 && redo_flag == S_FALSE)
164 {
165 size_t len = strlen(file);
166 char * path = SH_ALLOC(len + 2);
167 char * altres;
168
169 sl_strlcpy(path, file, len+2);
170 path[len] = '/'; path[len+1] = '\0';
171 auparse_destroy(au);
172
173 altres = doAuparse(path, time, tol, result, rsize, S_TRUE);
174
175 SH_FREE(path);
176 return altres;
177 }
178
179 if (0 == strcmp(state.success, "yes"))
180 {
181 char * tmp_exe = sh_util_safe_name(state.exe);
182 sl_snprintf(result, rsize,
183 _("time=%lu.%u, syscall=%s, auid=%u, uid=%u, gid=%u, euid=%u, egid=%u, fsuid=%u, fsgid=%u, exe=%s"),
184 (unsigned long) state.time, state.milli,
185 state.syscall,
186 state.auid, state.uid, state.gid, state.euid, state.egid,
187 state.fsuid, state.fsgid, tmp_exe);
188 SH_FREE(tmp_exe);
189 auparse_destroy(au);
190 return result;
191 }
192
193 err:
194 auparse_destroy(au);
195 return NULL;
196}
197
198static int sh_audit_checkdaemon();
199static int actl_pnum = -1;
200static char * actl_paths[4] =
201 {
202 N_("/sbin/auditctl"),
203 N_("/usr/sbin/auditctl"),
204 N_("/bin/auditctl"),
205 N_("/usr/bin/auditctl")
206 };
207
208
209/* Public function to fetch an audit record for path 'file', time 'time'
210 * The 'result' array should be sized ~256 char.
211 */
212char * sh_audit_fetch (char * file, time_t mtime, time_t ctime, char * result, size_t rsize)
213{
214 char * res = NULL;
215
216 if (sh_audit_checkdaemon() >= 0)
217 {
218 time_t new;
219 char buf[64];
220
221 if (mtime >= ctime) { new = mtime; }
222 else { new = ctime; }
223
224 res = doAuparse (file, new, 1, result, rsize, S_FALSE);
225
226 if (!res)
227 {
228 res = doAuparse (file, new, 3, result, rsize, S_FALSE);
229 }
230
231 }
232 return res;
233}
234
235void sh_audit_delete_all ()
236{
237 int p = sh_audit_checkdaemon();
238
239 if (p >= 0)
240 {
241 char ctl[64];
242
243 sl_snprintf(ctl, sizeof(ctl), _("%s -D -k samhain"),
244 _(actl_paths[p]));
245 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
246 0, MSG_E_SUBGEN,
247 _("Deleting audit daemon rules with key samhain"),
248 _("sh_audit_delete_all") );
249
250 sl_strlcpy(ctl, _(actl_paths[p]), sizeof(ctl));
251 sh_ext_system(ctl, ctl, "-D", "-k", _("samhain"), NULL);
252 }
253 return;
254}
255
256static int sh_audit_isdir(const char * file)
257{
258 struct stat buf;
259
260 if ( (0 == lstat (file, &buf)) && S_ISDIR(buf.st_mode))
261 return S_TRUE;
262 return S_FALSE;
263}
264
265static void sh_audit_mark_int (const char * file)
266{
267 static int flushRules = 0;
268
269 int p = sh_audit_checkdaemon();
270
271 /* Flush all rules at startup.
272 */
273 if (flushRules == 0)
274 {
275 sh_audit_delete_all ();
276 flushRules = 1;
277 }
278
279 if (p >= 0)
280 {
281 size_t len = strlen(file) + 64;
282 char * command = SH_ALLOC(len);
283 char * safe;
284 char ctl[64];
285 char a1[32];
286 char a2[32];
287 char a3[32];
288
289 sl_snprintf(command, len, _("%s -w %s -p wa -k samhain"),
290 _(actl_paths[p]),
291 file);
292
293 safe = sh_util_safe_name_keepspace(command);
294 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
295 0, MSG_E_SUBGEN,
296 safe,
297 _("sh_audit_mark") );
298
299 SH_FREE(safe);
300
301 sl_strlcpy(ctl, _(actl_paths[p]), sizeof(ctl));
302 sl_strlcpy(command, file, len);
303
304 sl_strlcpy(a3, _("samhain"), sizeof(a3));
305 sh_ext_system(ctl, ctl, "-w", command, "-p", "wa", "-k", a3, NULL);
306
307 /* Placing a watch on a directory will not place a watch on the
308 * directory inode, so we do this explicitely.
309 */
310 if (S_TRUE == sh_audit_isdir(file))
311 {
312 safe = sh_util_safe_name(file);
313 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
314 0, MSG_E_SUBGPATH,
315 _("Add path watch for directory"),
316 _("sh_audit_mark_int"), safe );
317 SH_FREE(safe);
318 sl_strlcpy(command, _("path="), len);
319 sl_strlcat(command, file, len);
320 sl_strlcpy(a1, _("always,exit"), sizeof(a1));
321 sl_strlcpy(a2, _("perm=wa"), sizeof(a2));
322 sh_ext_system(ctl, ctl, "-a", a1, "-F", command, "-F", a2, "-k", a3, NULL);
323 }
324 SH_FREE(command);
325 }
326 return;
327}
328
329struct aud_list {
330 char * file;
331 struct aud_list * next;
332};
333
334struct aud_list * mark_these = NULL;
335
336static void add_this (char * file)
337{
338 struct aud_list * this = SH_ALLOC(sizeof(struct aud_list));
339 size_t len = strlen(file);
340
341 this->file = sh_util_strdup(file);
342 if ((len > 1) && (file[len-1] == '/'))
343 this->file[len-1] = '\0';
344
345 this->next = mark_these;
346 mark_these = this;
347 return;
348}
349
350/* Check whether it is already covered by a higher directory
351 */
352static int test_exchange (struct aud_list * this, char * file)
353{
354 size_t len0 = sl_strlen(this->file);
355 size_t len1 = sl_strlen(file);
356 int ret = -1;
357
358 if (len0 == len1)
359 {
360 return strcmp(this->file, file);
361 }
362 else
363 {
364 char * s0 = SH_ALLOC(len0 + 2);
365 char * s1 = SH_ALLOC(len1 + 2);
366
367 sl_strlcpy(s0, this->file, len0 + 2);
368 sl_strlcpy(s1, file, len1 + 2);
369
370 if (s0 < s1)
371 {
372 sl_strlcat(s0, "/", len0 + 2);
373 ret = strncmp(s0, s1, len0 + 1);
374 }
375 else
376 {
377 sl_strlcat(s1, "/", len1 + 2);
378 if (0 == strncmp(s0, s1, len1 + 1))
379 {
380 size_t len = strlen(file);
381 SH_FREE(this->file);
382 this->file = sh_util_strdup(file);
383 if ((len > 1) && (file[len-1] == '/'))
384 this->file[len-1] = '\0';
385 ret = 0;
386 }
387 }
388 SH_FREE(s0);
389 SH_FREE(s1);
390 }
391
392 return ret;
393}
394
395/* Place a path on the list of of paths to be watched
396 */
397void sh_audit_mark (char * file)
398{
399 struct aud_list * this = mark_these;
400
401 if (!mark_these) {
402 add_this (file);
403 return;
404 }
405
406 while (this)
407 {
408 /* Check whether it is already covered by a higher
409 * directory
410 */
411 if (0 == test_exchange(this, file))
412 return;
413 this = this->next;
414 }
415
416 add_this (file);
417 return;
418}
419
420void sh_audit_commit ()
421{
422 struct aud_list * next;
423 struct aud_list * this = mark_these;
424
425 mark_these = NULL;
426
427 while (this)
428 {
429 sh_audit_mark_int (this->file);
430 next = this->next;
431 SH_FREE(this->file);
432 SH_FREE(this);
433 this = next;
434 }
435
436}
437
438static int sh_audit_checkdaemon()
439{
440 int i;
441 static int flag = 0;
442 char command[64];
443 char * p;
444
445 if (flag != 0)
446 return -1;
447
448 if (actl_pnum >= 0)
449 return actl_pnum;
450
451 for (i = 0; i < 4; ++i)
452 {
453 if (0 == access(_(actl_paths[i]), F_OK))/* flawfinder: ignore */
454 {
455 if (0 == access(_(actl_paths[i]), X_OK))/* flawfinder: ignore */
456 {
457 actl_pnum = i;
458 break;
459 }
460 else
461 {
462 char ebuf[SH_ERRBUF_SIZE];
463 int errnum = errno;
464
465 sl_snprintf(ebuf, sizeof(ebuf),
466 _("Cannot execute auditctl - %s\n"),
467 strerror(errnum));
468 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
469 errnum, MSG_E_SUBGEN,
470 ebuf,
471 _("sh_audit_checkdaemon") );
472 flag = 1;
473 actl_pnum = -1;
474 return -1;
475 }
476 }
477 }
478
479 if (actl_pnum == -1 && flag == 0)
480 {
481 char ebuf[SH_ERRBUF_SIZE];
482 int errnum = errno;
483
484 sl_snprintf(ebuf, sizeof(ebuf),
485 _("Cannot find auditctl - %s\n"),
486 strerror(errnum));
487 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
488 errnum, MSG_E_SUBGEN,
489 ebuf,
490 _("sh_audit_checkdaemon") );
491 flag = 1;
492 actl_pnum = -1;
493 return -1;
494 }
495
496 /* We found an executable auditctl */
497
498 sl_snprintf(command, sizeof(command), _("%s -s"), _(actl_paths[actl_pnum]));
499 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
500 0, MSG_E_SUBGEN,
501 command,
502 _("sh_audit_checkdaemon") );
503 p = sh_ext_popen_str (command);
504
505 if (p)
506 {
507 int retval = -1;
508 if (strstr(p, _(" pid=0 ")))
509 {
510 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
511 0, MSG_E_SUBGEN,
512 _("Audit daemon for Linux kernel audit system is not running"),
513 _("sh_audit_checkdaemon") );
514 flag = 1;
515 actl_pnum = -1;
516 }
517 else
518 {
519 retval = actl_pnum;
520 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
521 retval, MSG_E_SUBGEN,
522 _("Audit daemon is running"),
523 _("sh_audit_checkdaemon") );
524 }
525 SH_FREE(p);
526 return retval;
527 }
528
529 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
530 errno, MSG_E_SUBGEN,
531 _("No output from auditctl -s"),
532 _("sh_audit_checkdaemon") );
533 flag = 1;
534 actl_pnum = -1;
535 return -1;
536}
537
538/* HAVE_AUPARSE_H */
539#else
540char * sh_audit_fetch (char * file, time_t mtime, time_t ctime, char * result, size_t rsize)
541{
542 (void) file;
543 (void) mtime;
544 (void) ctime;
545 (void) result;
546 (void) rsize;
547
548 return 0;
549}
550void sh_audit_mark (const char * file)
551{
552 (void) file;
553 sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, 0, MSG_E_SUBGEN,
554 _("Setting audit watch not supported"),
555 _("sh_audit_mark"));
556 return;
557}
558void sh_audit_delete_all ()
559{
560 return;
561}
562void sh_audit_commit ()
563{
564 return;
565}
566#endif
567
568/* client || standalone */
569#endif
570
Note: See TracBrowser for help on using the repository browser.