source: trunk/src/sh_audit.c@ 536

Last change on this file since 536 was 516, checked in by katerina, 8 years ago

Fix for ticket #409 and #410 (unprivileged suidcheck and gcc 6.2 compiler warnings).

File size: 15.2 KB
RevLine 
[294]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>
[488]30#include <fcntl.h>
31#include <sys/stat.h>
[294]32
[488]33#include "samhain.h"
34#include "sh_error.h"
35
[489]36#undef FIL__
37#define FIL__ _("sh_audit.c")
38
[294]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
[488]109static char * doAuparse (const char * file, time_t time, int tol, char * result, size_t rsize, int redo_flag)
[294]110{
111 struct recordState state;
112 struct recordState stateFetched;
[488]113 unsigned int found_flag = 0;
114
[294]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 {
[488]138 ausearch_add_timestamp_item(au, ">=", time-tol, 0, AUSEARCH_RULE_AND);
139 ausearch_add_timestamp_item(au, "<=", time+tol, 0, AUSEARCH_RULE_AND);
[294]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));
[488]159 ++found_flag;
[294]160 }
161 auparse_next_event(au);
162 }
163
[488]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
[294]180 if (0 == strcmp(state.success, "yes"))
181 {
[514]182 char time_str[81];
[294]183 char * tmp_exe = sh_util_safe_name(state.exe);
[514]184
185 (void) sh_unix_gmttime (state.time, time_str, sizeof(time_str));
[294]186 sl_snprintf(result, rsize,
[514]187 _("time=%lu.%u, timestamp=%s, syscall=%s, auid=%u, uid=%u, gid=%u, euid=%u, egid=%u, fsuid=%u, fsgid=%u, exe=%s"),
188 (unsigned long) state.time, state.milli, time_str,
[294]189 state.syscall,
190 state.auid, state.uid, state.gid, state.euid, state.egid,
191 state.fsuid, state.fsgid, tmp_exe);
192 SH_FREE(tmp_exe);
193 auparse_destroy(au);
194 return result;
195 }
196
197 err:
198 auparse_destroy(au);
199 return NULL;
200}
201
[514]202#define SH_AUDIT_DEF "wa"
203static char sh_audit_flags[32] = SH_AUDIT_DEF;
204
205int sh_audit_set_flags(const char * str)
206{
207 if (!str || strlen(str) >= sizeof(sh_audit_flags))
208 return -1;
209 sl_strlcpy(sh_audit_flags, str, sizeof(sh_audit_flags));
210 return 0;
211}
212static void reset_audit_flags()
213{
214 sl_strlcpy(sh_audit_flags, SH_AUDIT_DEF, sizeof(sh_audit_flags));
215 return;
216}
217
218
[294]219static int sh_audit_checkdaemon();
220static int actl_pnum = -1;
221static char * actl_paths[4] =
222 {
223 N_("/sbin/auditctl"),
224 N_("/usr/sbin/auditctl"),
225 N_("/bin/auditctl"),
226 N_("/usr/bin/auditctl")
227 };
228
[514]229static char * getflags (char * file);
[294]230
231/* Public function to fetch an audit record for path 'file', time 'time'
232 * The 'result' array should be sized ~256 char.
233 */
[514]234char * sh_audit_fetch (char * file, time_t mtime, time_t ctime, time_t atime, char * result, size_t rsize)
[294]235{
[514]236 char * res = NULL;
237 char * flags = getflags(file);
[294]238
239 if (sh_audit_checkdaemon() >= 0)
240 {
[488]241 time_t new;
[294]242
[514]243 if (flags && (strchr(flags, 'r') || strchr(flags, 'x')) && atime >= ctime && atime >= mtime) { new = atime; }
244 else if (mtime >= ctime) { new = mtime; }
245 else { new = ctime; }
246
[488]247 res = doAuparse (file, new, 1, result, rsize, S_FALSE);
248
[294]249 if (!res)
250 {
[488]251 res = doAuparse (file, new, 3, result, rsize, S_FALSE);
[294]252 }
[488]253
[294]254 }
255 return res;
256}
257
258void sh_audit_delete_all ()
259{
260 int p = sh_audit_checkdaemon();
261
262 if (p >= 0)
263 {
[415]264 char ctl[64];
[294]265
[415]266 sl_snprintf(ctl, sizeof(ctl), _("%s -D -k samhain"),
[294]267 _(actl_paths[p]));
268 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
269 0, MSG_E_SUBGEN,
270 _("Deleting audit daemon rules with key samhain"),
271 _("sh_audit_delete_all") );
[415]272
273 sl_strlcpy(ctl, _(actl_paths[p]), sizeof(ctl));
274 sh_ext_system(ctl, ctl, "-D", "-k", _("samhain"), NULL);
[294]275 }
[514]276 reset_audit_flags();
[294]277 return;
278}
279
[488]280static int sh_audit_isdir(const char * file)
281{
282 struct stat buf;
283
284 if ( (0 == lstat (file, &buf)) && S_ISDIR(buf.st_mode))
285 return S_TRUE;
286 return S_FALSE;
287}
288
[514]289static void sh_audit_mark_int (const char * file, const char * flags)
[294]290{
291 static int flushRules = 0;
292
293 int p = sh_audit_checkdaemon();
294
295 /* Flush all rules at startup.
296 */
297 if (flushRules == 0)
298 {
299 sh_audit_delete_all ();
300 flushRules = 1;
301 }
302
303 if (p >= 0)
304 {
305 size_t len = strlen(file) + 64;
306 char * command = SH_ALLOC(len);
307 char * safe;
[415]308 char ctl[64];
[488]309 char a1[32];
310 char a2[32];
311 char a3[32];
[514]312 char a4[32];
[294]313
[514]314 sl_snprintf(command, len, _("%s -w %s -p %s -k samhain"),
315 _(actl_paths[p]), file, flags);
[294]316
317 safe = sh_util_safe_name_keepspace(command);
318 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
319 0, MSG_E_SUBGEN,
320 safe,
321 _("sh_audit_mark") );
[488]322
[294]323 SH_FREE(safe);
324
[415]325 sl_strlcpy(ctl, _(actl_paths[p]), sizeof(ctl));
326 sl_strlcpy(command, file, len);
327
[488]328 sl_strlcpy(a3, _("samhain"), sizeof(a3));
[514]329 sl_strlcpy(a4, flags, sizeof(a4));
330 sh_ext_system(ctl, ctl, "-w", command, "-p", a4, "-k", a3, NULL);
[415]331
[488]332 /* Placing a watch on a directory will not place a watch on the
333 * directory inode, so we do this explicitely.
334 */
335 if (S_TRUE == sh_audit_isdir(file))
336 {
337 safe = sh_util_safe_name(file);
338 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
339 0, MSG_E_SUBGPATH,
340 _("Add path watch for directory"),
341 _("sh_audit_mark_int"), safe );
342 SH_FREE(safe);
343 sl_strlcpy(command, _("path="), len);
344 sl_strlcat(command, file, len);
345 sl_strlcpy(a1, _("always,exit"), sizeof(a1));
[514]346 sl_strlcpy(a2, _("perm="), sizeof(a2));
347 sl_strlcat(a2, flags, sizeof(a2));
[488]348 sh_ext_system(ctl, ctl, "-a", a1, "-F", command, "-F", a2, "-k", a3, NULL);
349 }
[415]350 SH_FREE(command);
[294]351 }
352 return;
353}
354
[514]355#define SH_AU_FLAGS_SIZ 32
[415]356struct aud_list {
357 char * file;
[514]358 char flags[SH_AU_FLAGS_SIZ];
[415]359 struct aud_list * next;
360};
[294]361
[415]362struct aud_list * mark_these = NULL;
[514]363static int marked_committed = 0;
[415]364
[514]365static void delete_listofmarked()
366{
367 struct aud_list * tmp;
368 struct aud_list * this = mark_these;
369
370 mark_these = NULL;
371
372 while (this)
373 {
374 tmp = this;
375 this = this->next;
376
377 SH_FREE(tmp->file);
378 SH_FREE(tmp);
379 }
380 marked_committed = 0;
381}
382
383static char * getflags (char * file)
384{
385 struct aud_list * this = mark_these;
386
387 while (this)
388 {
389 if (0 == strcmp(file, this->file))
390 return this->flags;
391 this = this->next;
392 }
393 /* no explicit rule for this file */
394 return NULL;
395}
396
[415]397static void add_this (char * file)
398{
399 struct aud_list * this = SH_ALLOC(sizeof(struct aud_list));
[488]400 size_t len = strlen(file);
401
[419]402 this->file = sh_util_strdup(file);
[514]403
404 /* strip trailing '/' */
[488]405 if ((len > 1) && (file[len-1] == '/'))
406 this->file[len-1] = '\0';
[514]407
408 sl_strlcpy(this->flags, sh_audit_flags, SH_AU_FLAGS_SIZ);
[488]409
[415]410 this->next = mark_these;
[434]411 mark_these = this;
[415]412 return;
413}
414
[488]415/* Check whether it is already covered by a higher directory
416 */
[516]417static int test_exchange (struct aud_list * this, const char * file)
[415]418{
419 size_t len0 = sl_strlen(this->file);
420 size_t len1 = sl_strlen(file);
421 int ret = -1;
422
[516]423 if (!file || !this || !this->file)
424 return 0;
425
[415]426 if (len0 == len1)
427 {
428 return strcmp(this->file, file);
429 }
430 else
431 {
[514]432 if (0 == strcmp(this->flags, sh_audit_flags))
433 {
434 char * s0 = SH_ALLOC(len0 + 2);
435 char * s1 = SH_ALLOC(len1 + 2);
436
437 sl_strlcpy(s0, this->file, len0 + 2);
438 sl_strlcpy(s1, file, len1 + 2);
439
440 if (s0 < s1)
441 {
442 sl_strlcat(s0, "/", len0 + 2);
443 ret = strncmp(s0, s1, len0 + 1);
444 }
445 else
446 {
447 sl_strlcat(s1, "/", len1 + 2);
448 if (0 == strncmp(s0, s1, len1 + 1))
449 {
450 size_t len = strlen(file);
451 SH_FREE(this->file);
452 this->file = sh_util_strdup(file);
453 if ((len > 1) && (file[len-1] == '/'))
454 this->file[len-1] = '\0';
455 ret = 0;
456 }
457 }
458 SH_FREE(s0);
459 SH_FREE(s1);
460 }
[415]461 }
462
463 return ret;
464}
465
[488]466/* Place a path on the list of of paths to be watched
467 */
[415]468void sh_audit_mark (char * file)
469{
[514]470 struct aud_list * this;
[415]471
[514]472 if (marked_committed != 0)
473 delete_listofmarked();
474
[415]475 if (!mark_these) {
476 add_this (file);
477 return;
478 }
479
[514]480 this = mark_these;
481
[415]482 while (this)
483 {
[488]484 /* Check whether it is already covered by a higher
485 * directory
486 */
[415]487 if (0 == test_exchange(this, file))
488 return;
[514]489
[415]490 this = this->next;
491 }
492
493 add_this (file);
494 return;
495}
496
497void sh_audit_commit ()
498{
499 struct aud_list * this = mark_these;
500
501 while (this)
502 {
[514]503 sh_audit_mark_int (this->file, this->flags);
504 this = this->next;
[415]505 }
[514]506 marked_committed = 1;
[415]507}
508
[294]509static int sh_audit_checkdaemon()
510{
511 int i;
512 static int flag = 0;
513 char command[64];
514 char * p;
515
516 if (flag != 0)
517 return -1;
518
519 if (actl_pnum >= 0)
520 return actl_pnum;
521
522 for (i = 0; i < 4; ++i)
523 {
[295]524 if (0 == access(_(actl_paths[i]), F_OK))/* flawfinder: ignore */
[294]525 {
[295]526 if (0 == access(_(actl_paths[i]), X_OK))/* flawfinder: ignore */
[294]527 {
528 actl_pnum = i;
529 break;
530 }
531 else
532 {
533 char ebuf[SH_ERRBUF_SIZE];
534 int errnum = errno;
535
536 sl_snprintf(ebuf, sizeof(ebuf),
537 _("Cannot execute auditctl - %s\n"),
538 strerror(errnum));
539 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
540 errnum, MSG_E_SUBGEN,
541 ebuf,
542 _("sh_audit_checkdaemon") );
543 flag = 1;
544 actl_pnum = -1;
545 return -1;
546 }
547 }
548 }
549
550 if (actl_pnum == -1 && flag == 0)
551 {
552 char ebuf[SH_ERRBUF_SIZE];
553 int errnum = errno;
554
555 sl_snprintf(ebuf, sizeof(ebuf),
556 _("Cannot find auditctl - %s\n"),
557 strerror(errnum));
558 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
559 errnum, MSG_E_SUBGEN,
560 ebuf,
561 _("sh_audit_checkdaemon") );
562 flag = 1;
563 actl_pnum = -1;
564 return -1;
565 }
566
567 /* We found an executable auditctl */
568
569 sl_snprintf(command, sizeof(command), _("%s -s"), _(actl_paths[actl_pnum]));
570 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
571 0, MSG_E_SUBGEN,
572 command,
573 _("sh_audit_checkdaemon") );
574 p = sh_ext_popen_str (command);
575
576 if (p)
577 {
578 int retval = -1;
579 if (strstr(p, _(" pid=0 ")))
580 {
581 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
582 0, MSG_E_SUBGEN,
583 _("Audit daemon for Linux kernel audit system is not running"),
584 _("sh_audit_checkdaemon") );
585 flag = 1;
586 actl_pnum = -1;
587 }
588 else
589 {
590 retval = actl_pnum;
591 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
592 retval, MSG_E_SUBGEN,
593 _("Audit daemon is running"),
594 _("sh_audit_checkdaemon") );
595 }
596 SH_FREE(p);
597 return retval;
598 }
599
600 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__,
601 errno, MSG_E_SUBGEN,
602 _("No output from auditctl -s"),
603 _("sh_audit_checkdaemon") );
604 flag = 1;
605 actl_pnum = -1;
606 return -1;
607}
608
609/* HAVE_AUPARSE_H */
610#else
[514]611char * sh_audit_fetch (char * file, time_t mtime, time_t ctime, time_t atime, char * result, size_t rsize)
[294]612{
613 (void) file;
[488]614 (void) mtime;
615 (void) ctime;
[514]616 (void) atime;
[294]617 (void) result;
618 (void) rsize;
619
620 return 0;
621}
[373]622void sh_audit_mark (const char * file)
[294]623{
624 (void) file;
[488]625 sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, 0, MSG_E_SUBGEN,
626 _("Setting audit watch not supported"),
627 _("sh_audit_mark"));
[294]628 return;
629}
630void sh_audit_delete_all ()
631{
632 return;
633}
[415]634void sh_audit_commit ()
635{
636 return;
637}
[514]638int sh_audit_set_flags(const char * str)
639{
640 (void) str;
641 return -1;
642}
[294]643#endif
644
645/* client || standalone */
646#endif
647
Note: See TracBrowser for help on using the repository browser.