source: trunk/src/sh_restrict.c@ 440

Last change on this file since 440 was 383, checked in by katerina, 13 years ago

Fix for ticket #281 (warnings from clang static analyzer).

File size: 15.6 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2011 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#ifndef NULL
23#if !defined(__cplusplus)
24#define NULL ((void*)0)
25#else
26#define NULL (0)
27#endif
28#endif
29
30#if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
31
32#ifdef HAVE_REGEX_H
33#include <sys/types.h>
34#include <regex.h>
35#endif
36#include <stdlib.h>
37#include <string.h>
38#include <ctype.h>
39
40#include "samhain.h"
41#include "sh_mem.h"
42#include "sh_error_min.h"
43#include "sh_string.h"
44#include "sh_utils.h"
45#include "sh_restrict.h"
46
47#define FIL__ _("sh_restrict.c")
48
49#define SH_COND_NOT (1 << 0)
50#define SH_COND_PREFIX (1 << 1)
51#define SH_COND_REGEX (1 << 2)
52#define SH_COND_SIZE (1 << 3)
53#define SH_COND_PERM (1 << 4)
54#define SH_COND_FTYPE (1 << 5)
55#define SH_COND_PINCL (1 << 6)
56
57#define SH_COND_MAX 6
58
59struct sh_restrict_cond {
60
61 unsigned char cond_type[SH_COND_MAX];
62
63#ifdef HAVE_REGEX_H
64 regex_t * cond_preg[SH_COND_MAX];
65#endif
66
67 char * cond_str[SH_COND_MAX];
68
69 UINT64 cond_int[SH_COND_MAX];
70
71 struct sh_restrict_cond * next;
72};
73
74static struct sh_restrict_cond * sh_restrict_list = NULL;
75
76extern int matches_filetype(SL_TICKET ft, char * test_type);
77
78#ifdef HAVE_REGEX_H
79static int matches_regex (const char * path, const regex_t * regex)
80{
81 SL_ENTER(_("matches_regex"));
82
83 if (0 == regexec(regex, path, 0, NULL, 0))
84 {
85 SL_RETURN(1, _("matches_regex"));
86 }
87 SL_RETURN(0, _("matches_regex"));
88}
89#else
90static int matches_string (const char * path, const char * string)
91{
92 SL_ENTER(_("matches_string"));
93
94 if (NULL == strstr(path, string))
95 {
96 SL_RETURN(0, _("matches_string"));
97 }
98 SL_RETURN(1, _("matches_string"));
99}
100#endif
101
102static int matches_prefix (const char * path, const char * prefix)
103{
104 size_t path_len;
105 size_t pref_len;
106
107 SL_ENTER(_("matches_prefix"));
108
109 if (path && prefix)
110 {
111 path_len = sl_strlen(path);
112 pref_len = sl_strlen(prefix);
113
114 if (path_len >= pref_len)
115 {
116 if (0 == strncmp(path, prefix, pref_len))
117 {
118 SL_RETURN(1, _("matches_prefix"));
119 }
120 }
121 }
122 SL_RETURN(0, _("matches_prefix"));
123}
124
125static int exceeds_size (UINT64 size, UINT64 maxsize)
126{
127 SL_ENTER(_("exceeds_size"));
128
129 if (size > maxsize)
130 {
131 SL_RETURN(1, _("exceeds_size"));
132 }
133 SL_RETURN(0, _("exceeds_size"));
134}
135
136static int matches_perm (UINT64 perm, UINT64 needed_perm)
137{
138 SL_ENTER(_("matches_perm"));
139
140 if (needed_perm == (perm & 07777))
141 {
142 SL_RETURN(1, _("matches_perm"));
143 }
144 SL_RETURN(0, _("matches_perm"));
145}
146
147static int includes_perm (UINT64 perm, UINT64 needed_perm)
148{
149 UINT64 tmp = perm & 07777;
150
151 SL_ENTER(_("includes_perm"));
152
153 if (needed_perm == (tmp & needed_perm))
154 {
155 SL_RETURN(1, _("includes_perm"));
156 }
157 SL_RETURN(0, _("includes_perm"));
158}
159
160static int sh_restrict_test(const char * path,
161 UINT64 size, UINT64 perm, SL_TICKET fh,
162 struct sh_restrict_cond * current)
163{
164 int i;
165 unsigned char flag;
166 int res = 0;
167
168 (void) fh;
169
170 SL_ENTER(_("sh_restrict_test"));
171
172 for (i = 0; i < SH_COND_MAX; ++i)
173 {
174 flag = current->cond_type[i];
175
176 if (flag != 0)
177 {
178 if ((flag & (SH_COND_PREFIX)) != 0) {
179 res = matches_prefix(path, current->cond_str[i]);
180 }
181 else if ((flag & (SH_COND_REGEX)) != 0) {
182#ifdef HAVE_REGEX_H
183 res = matches_regex(path, current->cond_preg[i]);
184#else
185 res = matches_string(path, current->cond_str[i]);
186#endif
187 }
188 else if ((flag & (SH_COND_SIZE)) != 0) {
189 res = exceeds_size(size, current->cond_int[i]);
190 }
191 else if ((flag & (SH_COND_PERM)) != 0) {
192 res = matches_perm(perm, current->cond_int[i]);
193 }
194 else if ((flag & (SH_COND_PINCL)) != 0) {
195 res = includes_perm(perm, current->cond_int[i]);
196 }
197 else if ((flag & (SH_COND_FTYPE)) != 0) {
198 res = matches_filetype(fh, current->cond_str[i]);
199 }
200
201 /* Does condition hold?
202 */
203 if ((flag & (SH_COND_NOT)) != 0) {
204 /*
205 * Condition negated, ok if false (res == 0)
206 */
207 if (0 != res) {
208 SL_RETURN(0, _("sh_restrict_this"));
209 }
210 }
211 else {
212 /* Condition ok if true (res != 0) */
213 if (0 == res) {
214 SL_RETURN(0, _("sh_restrict_this"));
215 }
216 }
217 }
218 else
219 {
220 break;
221 }
222 }
223
224 /* All conditions true, restricted
225 */
226 SL_RETURN(1, _("sh_restrict_this"));
227}
228
229/* >>>>>>>>>> Evaluate the list <<<<<<<<<< */
230
231int sh_restrict_this(const char * path, UINT64 size, UINT64 perm, SL_TICKET fh)
232{
233 struct sh_restrict_cond * current = sh_restrict_list;
234
235 SL_ENTER(_("sh_restrict_this"));
236
237 if (!current)
238 {
239 SL_RETURN(0, _("sh_restrict_this"));
240 }
241
242 while (current)
243 {
244 if (0 != sh_restrict_test(path, size, perm, fh, current))
245 {
246 /* The current conditions are true, restricted
247 */
248 SL_RETURN(1, _("sh_restrict_this"));
249 }
250 current = current->next;
251 }
252
253 SL_RETURN(0, _("sh_restrict_this"));
254}
255
256
257/* >>>>>>>>>> Purge the list <<<<<<<<<< */
258
259static void sh_restrict_delete (struct sh_restrict_cond * current)
260{
261 int i;
262
263 if (current->next)
264 {
265 sh_restrict_delete(current->next);
266 }
267
268 for (i = 0; i < SH_COND_MAX; ++i)
269 {
270 if (current->cond_str[i]) {
271 SH_FREE(current->cond_str[i]);
272 }
273#ifdef HAVE_REGEX_H
274 if (current->cond_preg[i]) {
275 regfree(current->cond_preg[i]);
276 SH_FREE(current->cond_preg[i]);
277 }
278#endif
279 }
280 SH_FREE(current);
281 return;
282}
283
284void sh_restrict_purge ()
285{
286 struct sh_restrict_cond * current = sh_restrict_list;
287
288 sh_restrict_list = NULL;
289 if (current)
290 sh_restrict_delete(current);
291
292 sh_restrict_add_ftype(NULL);
293
294 return;
295}
296
297/* >>>>>>>>>> Create the list <<<<<<<<<< */
298
299static char * get_com(char * str)
300{
301 char * s;
302 char * e;
303
304 /* skip leading WS
305 */
306 for (s = str; *s && isspace((int)*s); ++s) /* nothing */;
307
308 e = strchr(s, '(');
309 if (e && (e != s))
310 {
311 *e = '\0'; --e;
312 while ( (e != s) && isspace((int)*e) )
313 {
314 *e = '\0'; --e;
315 }
316 if (e != s)
317 return s;
318 }
319 return NULL;
320}
321
322static char * get_arg(char * str)
323{
324 char * s;
325 char * e;
326
327 s = strchr(str, '(');
328
329 if (s)
330 {
331 ++s;
332
333 /* skip leading WS
334 */
335 for (; *s && isspace((int)*s); ++s) /* nothing */;
336
337 e = strrchr(s, ')');
338 if (e && (e != s))
339 {
340 /* strip trailing space */
341 *e = '\0'; --e;
342 while ( (e != s) && isspace((int)*e) )
343 {
344 *e = '\0'; --e;
345 }
346
347 if (e != s)
348 return s;
349 }
350 }
351 return NULL;
352}
353
354static int set_cond(struct sh_restrict_cond * current, int i,
355 char * com, char * arg)
356{
357 if (!com || !arg || (i >= SH_COND_MAX))
358 return -1;
359
360 if (0 == strcmp(com, _("match_prefix")))
361 {
362 current->cond_str[i] = sh_util_strdup(arg);
363 current->cond_type[i] |= SH_COND_PREFIX;
364 }
365 else if (0 == strcmp(com, _("match_regex")))
366 {
367#ifdef HAVE_REGEX_H
368 regex_t * preg = SH_ALLOC(sizeof(regex_t));
369
370 if (0 != regcomp(preg, arg, REG_NOSUB|REG_EXTENDED))
371 {
372 SH_FREE(preg);
373 return (-1);
374 }
375 current->cond_preg[i] = preg;
376#else
377 current->cond_str[i] = sh_util_strdup(arg);
378#endif
379 current->cond_type[i] |= SH_COND_REGEX;
380 }
381 else if (0 == strcmp(com, _("size_exceeds")))
382 {
383 current->cond_int[i] = (UINT64) strtoul(arg, (char **) NULL, 0);
384 current->cond_type[i] |= SH_COND_SIZE;
385 }
386 else if (0 == strcmp(com, _("match_permission")))
387 {
388 current->cond_int[i] = (UINT64) strtoul(arg, (char **) NULL, 8);
389 current->cond_type[i] |= SH_COND_PERM;
390 }
391 else if (0 == strcmp(com, _("have_permission")))
392 {
393 current->cond_int[i] = (UINT64) strtoul(arg, (char **) NULL, 8);
394 current->cond_type[i] |= SH_COND_PINCL;
395 }
396 else if (0 == strcmp(com, _("match_filetype")))
397 {
398 current->cond_str[i] = sh_util_strdup(arg);
399 current->cond_type[i] |= SH_COND_FTYPE;
400 }
401 else
402 {
403 return (-1);
404 }
405 return 0;
406}
407
408/* Format is [!]cond1(arg), cond2(arg), ...
409 */
410int sh_restrict_define(const char * str)
411{
412 SL_ENTER(_("sh_restrict_define"));
413
414 if (str)
415 {
416 size_t lengths[SH_COND_MAX];
417 unsigned int nfields = SH_COND_MAX;
418 char ** array;
419 sh_string * def = sh_string_new_from_lchar(str, strlen(str));
420
421 array = split_array_list(sh_string_str(def), &nfields, lengths);
422
423 if (array && nfields > 0)
424 {
425 char * p;
426 char * q;
427 unsigned int i;
428 struct sh_restrict_cond * current =
429 SH_ALLOC(sizeof(struct sh_restrict_cond));
430
431 current->next = NULL;
432 for (i = 0; i < SH_COND_MAX; ++i)
433 {
434 current->cond_int[i] = 0;
435 current->cond_type[i] = 0;
436 current->cond_str[i] = NULL;
437#ifdef HAVE_REGEX_H
438 current->cond_preg[i] = NULL;
439#endif
440 }
441
442 for (i = 0; i < nfields; ++i)
443 {
444 if (i == SH_COND_MAX)
445 {
446 sh_restrict_delete (current);
447 sh_string_destroy(&def);
448 SH_FREE(array);
449 SL_RETURN((-1), _("sh_restrict_define"));
450 }
451
452 p = array[i];
453
454 if (*p == '!')
455 {
456 current->cond_type[i] |= SH_COND_NOT;
457 ++p;
458 }
459
460 q = get_arg(p);
461 p = get_com(p);
462
463 if (!q || !p || (0 != set_cond(current, i, p, q)))
464 {
465 sh_restrict_delete (current);
466 sh_string_destroy(&def);
467 SH_FREE(array);
468 SL_RETURN((-1), _("sh_restrict_define"));
469 }
470 }
471
472 SH_FREE(array);
473
474 current->next = sh_restrict_list;
475 sh_restrict_list = current;
476 }
477
478 sh_string_destroy(&def);
479 SL_RETURN(0, _("sh_restrict_define"));
480 }
481
482 SL_RETURN((-1), _("sh_restrict_define"));
483}
484
485
486/* #if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE) */
487#endif
488
489#ifdef SH_CUTEST
490#include "CuTest.h"
491
492void Test_restrict (CuTest *tc) {
493
494#if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
495
496 char str[256];
497 char * p;
498 char * q;
499 int res;
500 SL_TICKET fd;
501 char buf[1024];
502
503 strcpy(str, "match(this)");
504 p = get_arg(str);
505 q = get_com(str);
506 CuAssertPtrNotNull(tc, p);
507 CuAssertPtrNotNull(tc, q);
508 CuAssertStrEquals(tc, "match", q);
509 CuAssertStrEquals(tc, "this", p);
510
511 strcpy(str, " match( this)");
512 p = get_arg(str);
513 q = get_com(str);
514 CuAssertPtrNotNull(tc, p);
515 CuAssertPtrNotNull(tc, q);
516 CuAssertStrEquals(tc, "match", q);
517 CuAssertStrEquals(tc, "this", p);
518
519 strcpy(str, " match ( this ) ");
520 p = get_arg(str);
521 q = get_com(str);
522 CuAssertPtrNotNull(tc, p);
523 CuAssertPtrNotNull(tc, q);
524 CuAssertStrEquals(tc, "match", q);
525 CuAssertStrEquals(tc, "this", p);
526
527 strcpy(str, " match (this ) ");
528 p = get_arg(str);
529 q = get_com(str);
530 CuAssertPtrNotNull(tc, p);
531 CuAssertPtrNotNull(tc, q);
532 CuAssertStrEquals(tc, "match", q);
533 CuAssertStrEquals(tc, "this", p);
534
535 strcpy(str, "size_exceeds(800), match_prefix(/home), match_regex(.*\\.mpg) ");
536 CuAssertTrue(tc, sh_restrict_list == NULL);
537 res = sh_restrict_define(str);
538 CuAssertIntEquals(tc,0,res);
539 CuAssertPtrNotNull(tc, sh_restrict_list);
540
541 sh_restrict_purge();
542 CuAssertTrue(tc, sh_restrict_list == NULL);
543
544 strcpy(str, "size_exceeds(800), match_prefix(/home), match_regex(.*\\.mpg), match_permission(0755) ");
545 CuAssertTrue(tc, sh_restrict_list == NULL);
546 res = sh_restrict_define(str);
547 CuAssertIntEquals(tc,0,res);
548 CuAssertPtrNotNull(tc, sh_restrict_list);
549
550 strcpy(str, "size_exceeds(800), match_prefix(/foo), have_permission(0100)");
551 res = sh_restrict_define(str);
552 CuAssertIntEquals(tc,0,res);
553 CuAssertPtrNotNull(tc, sh_restrict_list);
554
555 res = sh_restrict_this("/home/foo.mpg", 1000, 0755, 0);
556 CuAssertIntEquals(tc,1,res);
557
558 res = sh_restrict_this("/foo.mpg", 1000, 0755, 0);
559 CuAssertIntEquals(tc,1,res);
560
561 /* size too small */
562 res = sh_restrict_this("/foo.mpg", 600, 0755, 0);
563 CuAssertIntEquals(tc,0,res);
564
565 /* no execute permission */
566 res = sh_restrict_this("/foo.mpg", 600, 0644, 0);
567 CuAssertIntEquals(tc,0,res);
568
569 /* regex does not match */
570 res = sh_restrict_this("/home/foo", 1000, 0755, 0);
571 CuAssertIntEquals(tc,0,res);
572
573 /* wrong permission */
574 res = sh_restrict_this("/home/foo.mpg", 1000, 0705, 0);
575 CuAssertIntEquals(tc,0,res);
576
577 /* size too small */
578 res = sh_restrict_this("/home/foo.mpg", 600, 0755, 0);
579 CuAssertIntEquals(tc,0,res);
580
581 /* wrong prefix */
582 res = sh_restrict_this("/hoff/foo.mpg", 1000, 0755, 0);
583 CuAssertIntEquals(tc,0,res);
584
585 sh_restrict_purge();
586 CuAssertTrue(tc, sh_restrict_list == NULL);
587
588 fd = sl_open_fastread(FIL__, __LINE__, "/bin/sh", SL_NOPRIV);
589 CuAssertTrue(tc, fd > 0);
590
591 strcpy(str, "match_prefix(/bin), match_filetype(EXECUTABLE:UNIX:ELF)");
592 res = sh_restrict_define(str);
593 CuAssertIntEquals(tc,0,res);
594 CuAssertPtrNotNull(tc, sh_restrict_list);
595
596#if !defined(HOST_IS_CYGWIN)
597 res = sh_restrict_this("/bin/sh", 1000, 0755, fd);
598 CuAssertIntEquals(tc,1,res);
599#endif
600
601 sl_close(fd);
602
603 sh_restrict_purge();
604 CuAssertTrue(tc, sh_restrict_list == NULL);
605
606 strcpy(str, "match_filetype(FILE:TEXT:COPYING)");
607 res = sh_restrict_define(str);
608 CuAssertIntEquals(tc,0,res);
609 CuAssertPtrNotNull(tc, sh_restrict_list);
610
611 p = getcwd(buf, sizeof(buf));
612 CuAssertPtrNotNull(tc, p);
613
614 strcpy(str, "0:0:0:FILE:TEXT:COPYING:Copying:=0a=53=41=4d=48=41=49=4e");
615 res = sh_restrict_add_ftype(str);
616 CuAssertIntEquals(tc,0,res);
617
618 sl_strlcat(buf, "/COPYING", sizeof(buf));
619 fd = sl_open_fastread(FIL__, __LINE__, buf, SL_NOPRIV);
620 CuAssertTrue(tc, fd > 0);
621
622 res = sh_restrict_this(buf, 1000, 0755, fd);
623 CuAssertIntEquals(tc,1,res);
624
625 res = sh_restrict_add_ftype(str);
626 CuAssertIntEquals(tc,0,res);
627 res = sh_restrict_add_ftype(str);
628 CuAssertIntEquals(tc,0,res);
629 res = sh_restrict_add_ftype(str);
630 CuAssertIntEquals(tc,0,res);
631
632 res = sh_restrict_add_ftype(str);
633 CuAssertIntEquals(tc,0,res);
634 res = sh_restrict_add_ftype(str);
635 CuAssertIntEquals(tc,0,res);
636 res = sh_restrict_add_ftype(str);
637 CuAssertIntEquals(tc,0,res);
638 res = sh_restrict_add_ftype(str);
639 CuAssertIntEquals(tc,0,res);
640
641 res = sh_restrict_add_ftype(str);
642 CuAssertIntEquals(tc,0,res);
643 res = sh_restrict_add_ftype(str);
644 CuAssertIntEquals(tc,0,res);
645 res = sh_restrict_add_ftype(str);
646 CuAssertIntEquals(tc,0,res);
647 res = sh_restrict_add_ftype(str);
648 CuAssertIntEquals(tc,0,res);
649
650 res = sh_restrict_add_ftype(str);
651 CuAssertIntEquals(tc,0,res);
652 res = sh_restrict_add_ftype(str);
653 CuAssertIntEquals(tc,0,res);
654 res = sh_restrict_add_ftype(str);
655 CuAssertIntEquals(tc,0,res);
656 res = sh_restrict_add_ftype(str);
657 CuAssertIntEquals(tc,0,res);
658
659 res = sh_restrict_add_ftype(str);
660 CuAssertIntEquals(tc,-1,res);
661
662 sh_restrict_purge();
663 CuAssertTrue(tc, sh_restrict_list == NULL);
664
665 res = sh_restrict_add_ftype(str);
666 CuAssertIntEquals(tc,0,res);
667
668#else
669 (void) tc;
670/* #if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE) */
671#endif
672
673}
674#endif
675
676
Note: See TracBrowser for help on using the repository browser.