source: trunk/src/sh_audit.c@ 494

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

Version 4.1.0 final.

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#undef FIL__
37#define FIL__ _("sh_audit.c")
38
39#if !defined(SH_COMPILE_STATIC) && defined(__linux__) && defined(HAVE_AUPARSE_H) && defined(HAVE_AUPARSE_LIB)
40#include <auparse.h>
41
42#include "sh_extern.h"
43#include "sh_utils.h"
44
45
46#define REC_SIZE_SYSCALL 32
47#define REC_SIZE_EXE 64
48#define REC_SIZE_SUCCESS 32
49
50struct recordState {
51 char syscall[REC_SIZE_SYSCALL];
52 char exe[REC_SIZE_EXE];
53 char success[REC_SIZE_SUCCESS];
54 unsigned int auid;
55 unsigned int uid;
56 unsigned int gid;
57 unsigned int euid;
58 unsigned int egid;
59 unsigned int fsuid;
60 unsigned int fsgid;
61 time_t time;
62 unsigned int milli;
63};
64
65static int listRecords (auparse_state_t * au, struct recordState * state)
66{
67 if (auparse_first_record(au) != 1)
68 return -1;
69
70 state->time = auparse_get_time(au);
71 state->milli = auparse_get_milli(au);
72
73 if (auparse_find_field(au, _("syscall")))
74 sl_strlcpy(state->syscall, auparse_interpret_field(au), REC_SIZE_SYSCALL);
75
76 if (auparse_find_field(au, _("success")))
77 strncpy(state->success, auparse_interpret_field(au), REC_SIZE_SUCCESS);
78
79 if (auparse_find_field(au, "uid"))
80 state->uid = auparse_get_field_int(au);
81 if (auparse_find_field(au, "gid"))
82 state->gid = auparse_get_field_int(au);
83
84 if (auparse_find_field(au, _("euid")))
85 state->euid = auparse_get_field_int(au);
86 if (auparse_find_field(au, _("fsuid")))
87 state->fsuid = auparse_get_field_int(au);
88
89 auparse_first_field(au);
90
91 if (auparse_find_field(au, _("auid")))
92 state->auid = auparse_get_field_int(au);
93
94 auparse_first_field(au);
95
96 if (auparse_find_field(au, _("egid")))
97 state->egid = auparse_get_field_int(au);
98 if (auparse_find_field(au, _("fsgid")))
99 state->fsgid = auparse_get_field_int(au);
100
101 auparse_first_field(au);
102
103 if (auparse_find_field(au, "exe"))
104 sl_strlcpy(state->exe, auparse_interpret_field(au), REC_SIZE_EXE);
105
106 return 0;
107}
108
109static char * doAuparse (const char * file, time_t time, int tol, char * result, size_t rsize, int redo_flag)
110{
111 struct recordState state;
112 struct recordState stateFetched;
113 unsigned int found_flag = 0;
114
115 auparse_state_t * au = auparse_init(AUSOURCE_LOGS, NULL);
116
117 if (!au)
118 {
119 char ebuf[SH_ERRBUF_SIZE];
120 int errnum = errno;
121
122 sl_snprintf(ebuf, sizeof(ebuf), _("Error in auparse_init() - %s\n"),
123 strerror(errnum));
124 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, errnum, MSG_E_SUBGEN,
125 ebuf,
126 _("doAuparse") );
127 return NULL;
128 }
129
130 if (ausearch_add_interpreted_item(au, _("name"), "=", file,
131 AUSEARCH_RULE_CLEAR) != 0)
132 {
133 goto err;
134 }
135
136 if (time != 0)
137 {
138 ausearch_add_timestamp_item(au, ">=", time-tol, 0, AUSEARCH_RULE_AND);
139 ausearch_add_timestamp_item(au, "<=", time+tol, 0, AUSEARCH_RULE_AND);
140 }
141
142 if (ausearch_set_stop(au, AUSEARCH_STOP_RECORD) != 0)
143 {
144 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
145 _("Error in ausearch_set_stop\n"),
146 _("doAuparse") );
147 goto err;
148 }
149
150 memset(&state, '\0', sizeof(state));
151
152 while (ausearch_next_event(au) == 1)
153 {
154 memset(&stateFetched, '\0', sizeof(state));
155 listRecords(au, &stateFetched);
156 if (0 == strcmp(stateFetched.success, "yes"))
157 {
158 memcpy(&state, &stateFetched, sizeof(state));
159 ++found_flag;
160 }
161 auparse_next_event(au);
162 }
163
164 if (found_flag == 0 && redo_flag == S_FALSE)
165 {
166 size_t len = strlen(file);
167 char * path = SH_ALLOC(len + 2);
168 char * altres;
169
170 sl_strlcpy(path, file, len+2);
171 path[len] = '/'; path[len+1] = '\0';
172 auparse_destroy(au);
173
174 altres = doAuparse(path, time, tol, result, rsize, S_TRUE);
175
176 SH_FREE(path);
177 return altres;
178 }
179
180 if (0 == strcmp(state.success, "yes"))
181 {
182 char * tmp_exe = sh_util_safe_name(state.exe);
183 sl_snprintf(result, rsize,
184 _("time=%lu.%u, syscall=%s, auid=%u, uid=%u, gid=%u, euid=%u, egid=%u, fsuid=%u, fsgid=%u, exe=%s"),
185 (unsigned long) state.time, state.milli,
186 state.syscall,
187 state.auid, state.uid, state.gid, state.euid, state.egid,
188 state.fsuid, state.fsgid, tmp_exe);
189 SH_FREE(tmp_exe);
190 auparse_destroy(au);
191 return result;
192 }
193
194 err:
195 auparse_destroy(au);
196 return NULL;
197}
198
199static int sh_audit_checkdaemon();
200static int actl_pnum = -1;
201static char * actl_paths[4] =
202 {
203 N_("/sbin/auditctl"),
204 N_("/usr/sbin/auditctl"),
205 N_("/bin/auditctl"),
206 N_("/usr/bin/auditctl")
207 };
208
209
210/* Public function to fetch an audit record for path 'file', time 'time'
211 * The 'result' array should be sized ~256 char.
212 */
213char * sh_audit_fetch (char * file, time_t mtime, time_t ctime, char * result, size_t rsize)
214{
215 char * res = NULL;
216
217 if (sh_audit_checkdaemon() >= 0)
218 {
219 time_t new;
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.