1 /* SAMHAIN file system integrity testing                                   */
2 /* Copyright (C) 2002 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 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 
31 /*
32    gcc -Wall -O2 -o mysched sh_schedule.c -DTESTONLY
33  */
34 #ifndef TESTONLY
35 
36 
37 #undef  FIL__
38 #define FIL__  _("sh_schedule.c")
39 
40 #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
41 #define SCHEDULER_YES
42 #endif
43 
44 #if TIME_WITH_SYS_TIME
45 #include <sys/time.h>
46 #include <time.h>
47 #else
48 #if HAVE_SYS_TIME_H
49 #include <sys/time.h>
50 #else
51 #include <time.h>
52 #endif
53 #endif
54 
55 #include "samhain.h"
56 #include "sh_mem.h"
57 #include "sh_error_min.h"
58 
59 /* TESTONLY */
60 #else
61 
62 #define SCHEDULER_YES
63 #include <time.h>
64 
65 #endif
66 
67 #include "sh_schedule.h"
68 
69 
70 
71 #ifdef SCHEDULER_YES
72 
73 /************************************************
74  *
75  * Scheduler class - private area
76  *
77  ************************************************/
78 
79 
80 static const int  sh_schedule_max[5] = { 59, 23, 31, 12, 7 };
81 static const int  sh_schedule_min[5] = {  0,  0,  0,  0, 0 };
82 
83 static
test_val(int i,int min,int max,int min_step,time_t * last,time_t now,int nval,int first_flag)84 int test_val (int i, int min, int max, int min_step,
85 	      time_t * last, time_t now, int nval, int first_flag)
86 {
87   /* don't miss a minute's task
88    * IDEA:  set last = now after first check (? seems to work)
89    */
90   if (i == 0 && max == min && nval > max
91       /* && ( ((now - *last) > min_step) || (*last == (time_t)-1) ) */ )
92     {
93       if (*last == (time_t)-1)
94 	{
95 	  /* fake execution at nval-max
96 	   */
97 	  *last = now - 60 * (nval-max);
98 	  return 0;
99 	}
100       if ((int)(now - *last) > min_step)
101 	return 1;
102     }
103 
104   /* out of range
105    */
106   if (nval > max || nval < min)
107     return 0;
108 
109   /* first call - invalid last_exec
110 
111   if (*last == (time_t)-1)
112     return 1;
113   */
114 
115   if (first_flag == 0)
116     return 1;
117 
118 
119   /* before min_step - too early (e.g. same minute)
120    */
121   if ((int)(now - *last) <= min_step)
122     return 0;
123 
124   return 1;
125 }
126 
127 static
test_sched_int(sh_schedule_t * isched)128 int test_sched_int (sh_schedule_t * isched)
129 {
130   time_t now;
131   struct tm * tval;
132   int count, i, nval;
133 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_LOCALTIME_R)
134   struct tm     time_tm;
135 #endif
136 
137   if (!isched)
138     return 0;
139 
140   now  = time(NULL);
141 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_LOCALTIME_R)
142   tval = localtime_r(&now, &time_tm);
143 #else
144   tval = localtime(&now);
145 #endif
146 
147   if (!tval)
148     {
149       sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
150                        _("localime() failed"), _("test_sched_int") );
151       return 0;
152     }
153 
154   count = 0;
155   for (i = 0; i < 5; ++i)
156     {
157       if      (i == 0) nval = tval->tm_min;
158       else if (i == 1) nval = tval->tm_hour;
159       else if (i == 2) nval = tval->tm_mday;
160       else if (i == 3) nval = tval->tm_mon;
161       else             nval = tval->tm_wday;
162       count += test_val (i, isched->min[i], isched->max[i],
163 			 isched->min_step, &(isched->last_exec),
164 			 now, nval, isched->first);
165     }
166 
167   if (count == 5)
168     {
169       isched->first = 1;
170       isched->last_exec = now;
171       return 1;
172     }
173 
174   return 0;
175 }
176 
177 /* test a linked list of schedules
178  */
test_sched(sh_schedule_t * isched)179 int test_sched (sh_schedule_t * isched)
180 {
181   sh_schedule_t * intern = isched;
182   int             retval = 0;
183 
184   while (intern != NULL)
185     {
186       if (test_sched_int(intern) == 1)
187 	retval = 1;
188       intern = intern->next;
189     }
190   return retval;
191 }
192 
193 static
194 char DayNames[7][4] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
195 static
196 char MonNames[12][4] = { "jan", "feb", "mar", "apr", "may", "jun",
197 		       "jul", "aug", "sep", "oct", "nov", "dec" };
198 
199 static
parse_func(int i,char * p)200 int parse_func (int i, char * p)
201 {
202   int j, k, l;
203   char *tail;
204 
205   errno = 0;
206   j = (int) strtol(p, &tail, 10);
207 
208   if (errno != 0)     /* overflow          */
209     return -1;
210   if (j < 0)
211     return -1;
212   if (tail != p)      /* numeric           */
213     return j;
214   if (i < 3)          /* names not allowed */
215     return -1;
216 
217   if (i == 3)
218     {
219       for (j = 0; j < 12; ++j) {
220 	l = 0;
221 	/*@+charint@*//* Incompatible types for == (char, char): ??? */
222 	for (k = 0; k < 3; ++k)
223 	  if (p[k] != '\0' && tolower((int) p[k]) == MonNames[j][k]) ++l;
224 	/*@-charint@*/
225 	if (l == 3)
226 	  return j;
227       }
228     }
229   if (i == 4)
230     {
231       for (j = 0; j < 7; ++j) {
232 	l = 0;
233 	/*@+charint@*//* Incompatible types for == (char, char): ??? */
234 	for (k = 0; k < 3; ++k)
235 	  if (p[k] != '\0' && tolower((int) p[k]) == DayNames[j][k]) ++l;
236 	/*@-charint@*/
237 	if (l == 3)
238 	  return j;
239       }
240     }
241 
242   return -1;
243 }
244 
245 static
parse_token(int i,sh_schedule_t * isched,char * p)246 int parse_token(int i, sh_schedule_t * isched, char * p)
247 {
248   char * q;
249 
250   if ( NULL != (q = strchr(p, ',')))
251     return -1;
252 
253   if (*p == '*')
254     {
255       isched->min[i] = sh_schedule_min[i];
256       isched->max[i] = sh_schedule_max[i];
257     }
258   else
259     {
260       isched->min[i] = parse_func(i, p);
261       if (i == 4 && isched->min[i] == 7)
262 	isched->min[i] = 0;
263       if (isched->min[i] < sh_schedule_min[i] ||
264 	  isched->min[i] > sh_schedule_max[i])
265 	{
266 	  return -1;
267 	}
268       if ( NULL != (q = strchr(p, '-')))
269 	{
270 	  ++q;
271 	  isched->max[i] = parse_func(i, q);
272 	  if (i == 4 && isched->max[i] == 7)
273 	    isched->max[i] = 0;
274 	  if (isched->max[i] < sh_schedule_min[i] ||
275 	      isched->max[i] > sh_schedule_max[i] ||
276 	      isched->max[i] < isched->min[i])
277 	    {
278 	      return -1;
279 	    }
280 	}
281       else
282 	isched->max[i] = isched->min[i];
283     }
284 
285   if ( NULL != (q = strchr(p, '/')))
286     {
287       ++q;
288       isched->step[i] = atoi(q);
289       if (isched->step[i] < 1 || isched->step[i] > sh_schedule_max[i])
290 	{
291 	  return -1;
292 	}
293       if (i == 4 && isched->step[i] == 7)
294 	isched->step[i] = 6;
295     }
296   else
297     {
298       isched->step[i] = 1;
299     }
300 
301   switch (i)
302     {
303     case 0:
304       if (isched->max[i] == isched->min[i])
305 	isched->min_step = 3599;
306       else
307 	isched->min_step = (isched->step[i] * 60) - 1;
308       break;
309     case 1:
310       if (isched->max[i] == isched->min[i])
311 	{
312 	  /* fix for daylight saving time: subtract 3600 sec
313 	   */
314 	  if (isched->min_step == 3599)
315 	    isched->min_step = 86399 - 3600;
316 	}
317       else
318 	{
319 	  if (isched->min_step == 3599)
320 	    isched->min_step = (isched->step[i] * 3600) - 1;
321 	}
322       break;
323     default:
324       break;
325     }
326 
327   return 0;
328 }
329 
330 static
parse_sched(const char * ssched,sh_schedule_t * isched)331 int parse_sched (const char * ssched, sh_schedule_t * isched)
332 {
333   char * p;
334   char * copy;
335   int    i = 0;
336   size_t len;
337 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
338   char * saveptr;
339 #endif
340 
341   if (!ssched || !isched)
342     return -1;
343 
344   len = strlen(ssched)+1;
345 #ifdef TESTONLY
346   copy = calloc(1,len);                 /* testonly code */
347 #else
348   copy = SH_ALLOC(len);
349 #endif
350   sl_strlcpy(copy, ssched, len);
351 
352 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
353   p = strtok_r(copy, " \t", &saveptr); /* parse crontab-style schedule */
354 #else
355   p = strtok(copy, " \t"); /* parse crontab-style schedule */
356 #endif
357 
358   if (!p)
359     goto err;
360   if (parse_token(i, isched, p) == -1)
361     goto err;
362 
363   for (i = 1; i < 5; ++i)
364     {
365 #if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_STRTOK_R)
366       p = strtok_r(NULL, " \t", &saveptr); /* parse crontab-style schedule */
367 #else
368       p = strtok(NULL, " \t"); /* parse crontab-style schedule */
369 #endif
370       if (!p)
371 	goto err;
372       if (parse_token(i, isched, p) == -1)
373 	goto err;
374     }
375 
376   isched->last_exec = (time_t)-1;
377   isched->first     = 0;
378   isched->next      = NULL;
379 
380 #ifdef TESTONLY
381   free(copy);
382 #else
383   SH_FREE(copy);
384 #endif
385   return 0;
386 
387  err:
388 #ifdef TESTONLY
389   free(copy);
390 #else
391   SH_FREE(copy);
392 #endif
393   return -1;
394 }
395 
create_sched(const char * ssched,sh_schedule_t * isched)396 int create_sched (const char * ssched, sh_schedule_t * isched)
397 {
398   int j;
399 
400   if (!isched || !ssched)
401     return -1;
402 
403   j = parse_sched(ssched, isched);
404 
405 #ifdef TESTONLY
406   if (j == 0)
407     {
408       int i;
409       for (i = 0; i < 5; ++i)
410 	printf("%2d MIN  %3d  MAX  %3d  STEP  %3d\n",
411 	       i, isched->max[i], isched->min[i], isched->step[i]);
412       printf("MINSTEP   %7d\n", isched->min_step);
413       printf("LASTEXEC  %7ld\n", (long) isched->last_exec);
414     }
415 #endif
416 
417   return j;
418 }
419 
420 /* #ifdef SCHEDULER_YES */
421 #endif
422 
423 /**************************************************
424  *
425  * Schedule class - Test driver
426  *
427  **************************************************/
428 #ifdef TESTONLY
429 
main(int argc,char * argv[])430 int main(int argc, char * argv[])
431 {
432   sh_schedule_t isched;
433 
434   if (argc < 2)
435     {
436       fprintf(stderr, "Usage: %s 'schedule'\n", argv[0]);
437       exit (1);
438     }
439 
440   if (create_sched(argv[1], &isched) < 0)
441     {
442       fprintf(stderr, "Bad schedule <%s>\n", argv[1]);
443       exit (1);
444     }
445 
446   while (1 == 1)
447     {
448       if (test_sched(&isched))
449 	printf("EXECUTE  at: %s", ctime(&(isched.last_exec))); /* TESTONLY */
450       sleep (1); /* TESTONLY */
451     }
452   return 0;
453 }
454 #endif
455