1 /*
2 SSSD
3
4 IPA Provider Time Rules Parsing
5
6 Authors:
7 Jakub Hrozek <jhrozek@redhat.com>
8
9 Copyright (C) Red Hat, Inc 2009
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #define _XOPEN_SOURCE /* strptime() needs this */
26
27 #include <pcre.h>
28 #include <talloc.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <time.h>
34 #include <stdbool.h>
35 #include <limits.h>
36
37 #include "providers/ipa/ipa_timerules.h"
38 #include "util/util.h"
39
40 #define JMP_NEOK(variable) do { \
41 if (variable != EOK) goto done; \
42 } while (0)
43
44 #define JMP_NEOK_LABEL(variable, label) do { \
45 if (variable != EOK) goto label; \
46 } while (0)
47
48 #define CHECK_PTR(ptr) do { \
49 if (ptr == NULL) { \
50 return ENOMEM; \
51 } \
52 } while (0)
53
54 #define CHECK_PTR_JMP(ptr) do { \
55 if (ptr == NULL) { \
56 ret = ENOMEM; \
57 goto done; \
58 } \
59 } while (0)
60
61 #define BUFFER_OR_JUMP(ctx, ptr, count) do { \
62 ptr = talloc_array(ctx, unsigned char, count); \
63 if (ptr == NULL) { \
64 return ENOMEM; \
65 } \
66 memset(ptr, 0, sizeof(unsigned char)*count); \
67 } while (0)
68
69 #define TEST_BIT_RANGE(bitfield, index, resptr) do { \
70 if (bitfield) { \
71 if (test_bit(&bitfield, index) == 0) { \
72 *resptr = false; \
73 return EOK; \
74 } \
75 } \
76 } while (0)
77
78 #define TEST_BIT_RANGE_PTR(bitfield, index, resptr) do { \
79 if (bitfield) { \
80 if (test_bit(bitfield, index) == 0) { \
81 *resptr = false; \
82 return EOK; \
83 } \
84 } \
85 } while (0)
86
87 /* number of match offsets when matching pcre regexes */
88 #define OVEC_SIZE 30
89
90 /* regular expressions describing syntax of our HBAC grammar */
91 #define RGX_WEEKLY "day (?P<day_of_week>(0|1|2|3|4|5|6|7|Mon|Tue|Wed|Thu|Fri|Sat|Sun|,|-)+)"
92
93 #define RGX_MDAY "(?P<mperspec_day>day) (?P<interval_day>[0-9,-]+) "
94 #define RGX_MWEEK "(?P<mperspec_week>week) (?P<interval_week>[0-9,-]+) "RGX_WEEKLY
95 #define RGX_MONTHLY RGX_MDAY"|"RGX_MWEEK
96
97 #define RGX_YDAY "(?P<yperspec_day>day) (?P<day_of_year>[0-9,-]+) "
98 #define RGX_YWEEK "(?P<yperspec_week>week) (?P<week_of_year>[0-9,-]+) "RGX_WEEKLY
99 #define RGX_YMONTH "(?P<yperspec_month>month) (?P<month_number>[0-9,-]+) (?P<m_period>.*?)$"
100 #define RGX_YEARLY RGX_YMONTH"|"RGX_YWEEK"|"RGX_YDAY
101
102 #define RGX_TIMESPEC "(?P<timeFrom>[0-9]{4}) ~ (?P<timeTo>[0-9]{4})"
103
104 #define RGX_GENERALIZED "(?P<year>[0-9]{4})(?P<month>[0-9]{2})(?P<day>[0-9]{2})(?P<hour>[0-9]{2})?(?P<minute>[0-9]{2})?(?P<second>[0-9]{2})?"
105
106 #define RGX_PERIODIC "^periodic (?P<perspec>daily|weekly|monthly|yearly) (?P<period>.*?)"RGX_TIMESPEC"$"
107 #define RGX_ABSOLUTE "^absolute (?P<from>\\S+) ~ (?P<to>\\S+)$"
108
109 /* limits on various parameters */
110 #define DAY_OF_WEEK_MAX 7
111 #define DAY_OF_MONTH_MAX 31
112 #define WEEK_OF_MONTH_MAX 5
113 #define WEEK_OF_YEAR_MAX 54
114 #define DAY_OF_YEAR_MAX 366
115 #define MONTH_MAX 12
116 #define HOUR_MAX 23
117 #define MINUTE_MAX 59
118
119 /* limits on sizes of buffers for bit arrays */
120 #define DAY_OF_MONTH_BUFSIZE 8
121 #define DAY_OF_YEAR_BUFSIZE 44
122 #define WEEK_OF_YEAR_BUFSIZE 13
123 #define MONTH_BUFSIZE 2
124 #define HOUR_BUFSIZE 4
125 #define MINUTE_BUFSIZE 8
126
127 /* Lookup tables for translating names of days and months */
128 static const char *names_day_of_week[] =
129 { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", NULL };
130 static const char *names_months[] =
131 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
132 "Jul", "Aug", "Sep", "Nov", "Dec", NULL };
133
134 /*
135 * Timelib knows two types of ranges - periodic and absolute
136 */
137 enum rangetypes {
138 TYPE_ABSOLUTE,
139 TYPE_PERIODIC
140 };
141
142 struct absolute_range {
143 time_t time_from;
144 time_t time_to;
145 };
146
147 struct periodic_range {
148 unsigned char day_of_week;
149 unsigned char *day_of_month;
150 unsigned char *day_of_year;
151 unsigned char week_of_month;
152 unsigned char *week_of_year;
153 unsigned char *month;
154 unsigned char *hour;
155 unsigned char *minute;
156 };
157
158 /*
159 * Context of one time rule being analyzed
160 */
161 struct range_ctx {
162 /* main context with precompiled patterns */
163 struct time_rules_ctx *trctx;
164 /* enum rangetypes */
165 enum rangetypes type;
166
167 struct absolute_range *abs;
168 struct periodic_range *per;
169 };
170
171
172 /*
173 * The context of one regular expression
174 */
175 struct parse_ctx {
176 /* the regular expression used for one parsing */
177 pcre *re;
178 /* number of matches */
179 int matches;
180 /* vector of matches */
181 int *ovec;
182 };
183
184 /* indexes to the array of precompiled regexes */
185 enum timelib_rgx {
186 LP_RGX_GENERALIZED,
187 LP_RGX_MDAY,
188 LP_RGX_MWEEK,
189 LP_RGX_YEARLY,
190 LP_RGX_WEEKLY,
191 LP_RGX_ABSOLUTE,
192 LP_RGX_PERIODIC,
193 LP_RGX_MAX,
194 };
195
196 /* matches the indexes */
197 static const char *lookup_table[] = {
198 RGX_GENERALIZED,
199 RGX_MDAY,
200 RGX_MWEEK,
201 RGX_YEARLY,
202 RGX_WEEKLY,
203 RGX_ABSOLUTE,
204 RGX_PERIODIC,
205 NULL,
206 };
207
208 /*
209 * Main struct passed outside
210 * holds precompiled regular expressions
211 */
212 struct time_rules_ctx {
213 pcre *re[LP_RGX_MAX];
214 };
215
216 /*******************************************************************
217 * helper function - bit arrays *
218 *******************************************************************/
219
220 /* set a single bit in a bitmap */
221 static void set_bit(unsigned char *bitmap, unsigned int bit)
222 {
223 bitmap[bit/CHAR_BIT] |= 1 << (bit%CHAR_BIT);
224 }
225
226 /*
227 * This function is based on bit_nset macro written originally by Paul Vixie,
228 * copyrighted by The Regents of the University of California, as found
229 * in tarball of fcron, file bitstring.h
230 */
231 static void set_bit_range(unsigned char *bitmap, unsigned int start,
232 unsigned int stop)
233 {
234 int startbyte = start/CHAR_BIT;
235 int stopbyte = stop/CHAR_BIT;
236
237 if (startbyte == stopbyte) {
238 bitmap[startbyte] |= ((0xff << (start & 0x7)) &
239 (0xff >> (CHAR_BIT- 1 - (stop & 0x7))));
240 } else {
241 bitmap[startbyte] |= 0xff << (start & 0x7);
242 while (++startbyte < stopbyte) {
243 bitmap[startbyte] |= 0xff;
244 }
245 bitmap[stopbyte] |= 0xff >> (CHAR_BIT- 1 - (stop & 0x7));
246 }
247 }
248
249 static int test_bit(unsigned char *bitmap, unsigned int bit)
250 {
Event ptr_arith: Performing pointer arithmetic on "bitmap" in expression "bitmap + bit / 8U".
|
251 return (int)(bitmap[bit/CHAR_BIT] >> (bit%CHAR_BIT)) & 1;
252 }
253
254 /*******************************************************************
255 * parsing intervals *
256 *******************************************************************/
257
258 /*
259 * Some ranges allow symbolic names, like Mon..Sun for names of day.
260 * This routine takes a list of symbolic names as NAME_ARRAY and the
261 * one we're looking for as KEY and returns its index or -1 when not
262 * found. The last member of NAME_ARRAY must be NULL.
263 */
264 static int name_index(const char **name_array, const char *key, int min)
265 {
266 int index = 0;
267 const char *one;
268
269 if (name_array == NULL) {
270 return -1;
271 }
272
273 while ((one = name_array[index]) != NULL) {
274 if (strcmp(key,one) == 0) {
275 return index+min;
276 }
277 index++;
278 }
279
280 return -1;
281 }
282
283 /*
284 * Sets appropriate bits given by an interval in STR (in form of 1,5-7,10) to
285 * a bitfield given in OUT. Does no boundary checking. STR can also contain
286 * symbolic names, these would be given in TRANSLATE.
287 */
288 static int interval2bitfield(TALLOC_CTX *mem_ctx,
289 unsigned char *out,
290 const char *str,
291 int min, int max,
292 const char **translate)
293 {
294 char *copy;
295 char *next, *token;
296 int tokval, tokmax;
297 char *end_ptr;
298 int ret;
299 char *dash;
300
301 DEBUG(9, ("Converting '%s' to interval\n", str));
302
303 copy = talloc_strdup(mem_ctx, str);
304 CHECK_PTR(copy);
305
306 next = copy;
307 while (next) {
308 token = next;
309 next = strchr(next, ',');
310 if (next) {
311 *next = '\0';
312 next++;
313 }
314
315 errno = 0;
316 tokval = strtol(token, &end_ptr, 10);
317 if (*end_ptr == '\0' && errno == 0) {
318 if (tokval <= max && tokval >= 0) {
319 set_bit(out, tokval);
320 continue;
321 } else {
322 ret = ERANGE;
323 goto done;
324 }
325 } else if ((dash = strchr(token, '-')) != NULL){
326 *dash = '\0';
327 ++dash;
328
329 errno = 0;
330 tokval = strtol(token, &end_ptr, 10);
331 if (*end_ptr != '\0' || errno != 0) {
332 tokval = name_index(translate, token, min);
333 if (tokval == -1) {
334 ret = ERANGE;
335 goto done;
336 }
337 }
338 errno = 0;
339 tokmax = strtol(dash, &end_ptr, 10);
340 if (*end_ptr != '\0' || errno != 0) {
341 tokmax = name_index(translate, dash, min);
342 if (tokmax == -1) {
343 ret = ERANGE;
344 goto done;
345 }
346 }
347
348 if (tokval <= max && tokmax <= max &&
349 tokval >= min && tokmax >= min) {
350 if (tokmax > tokval) {
351 DEBUG(7, ("Setting interval %d-%d\n", tokval, tokmax));
352 DEBUG(9, ("interval: %p\n", out));
353 set_bit_range(out, tokval, tokmax);
354 } else {
355 /* Interval wraps around - i.e. from 18.00 to 06.00 */
356 DEBUG(7, ("Setting inverted interval %d-%d\n", tokval, tokmax));
357 DEBUG(9, ("interval: %p\n", out));
358 set_bit_range(out, min, tokmax);
359 set_bit_range(out, tokval, max);
360 }
361 continue;
362 } else {
363 /* tokval or tokmax are not between <min, max> */
364 ret = ERANGE;
365 goto done;
366 }
367 } else if ((tokval = name_index(translate, token, min)) != -1) {
368 /* Try to translate one token by name */
369 if (tokval <= max) {
370 set_bit(out, tokval);
371 continue;
372 } else {
373 ret = ERANGE;
374 goto done;
375 }
376 } else {
377 ret = EINVAL;
378 goto done;
379 }
380 }
381
382 ret = EOK;
383 done:
384 talloc_free(copy);
385 return ret;
386 }
387
388 /*******************************************************************
389 * wrappers around regexp handling *
390 *******************************************************************/
391
392 /*
393 * Copies a named substring SUBSTR_NAME from string STR using the parsing
394 * information from PCTX. The context PCTX is also used as a talloc context.
395 *
396 * The resulting string is stored in OUT.
397 * Return value is EOK on no error or ENOENT on error capturing the substring
398 */
399 static int copy_substring(struct parse_ctx *pctx,
400 const char *str,
401 const char *substr_name,
402 char **out)
403 {
404 const char *result = NULL;
405 int ret;
406 char *o = NULL;
407
408 result = NULL;
409
410 ret = pcre_get_named_substring(pctx->re, str, pctx->ovec,
411 pctx->matches, substr_name, &result);
412 if (ret < 0 || result == NULL) {
413 DEBUG(5, ("named substring '%s' does not exist in '%s'\n",
414 substr_name, str));
415 return ENOENT;
416 }
417
418 o = talloc_strdup(pctx, result);
419 pcre_free_substring(result);
420 if (o == NULL) {
421 return ENOMEM;
422 }
423
424 DEBUG(9, ("Copied substring named '%s' value '%s'\n", substr_name, o));
425
426 *out = o;
427 return EOK;
428 }
429
430 /*
431 * Copies a named substring SUBSTR_NAME from string STR using the parsing
432 * information from PCTX and converts it to an integer.
433 * The context PCTX is also used as a talloc context.
434 *
435 * The resulting string is stored in OUT.
436 * Return value is EOK on no error or ENOENT on error capturing the substring
437 */
438 static int substring_strtol(struct parse_ctx *pctx,
439 const char *str,
440 const char *substr_name,
441 int *out)
442 {
443 char *substr = NULL;
444 int ret;
445 int val;
446 char *err_ptr;
447
448 ret = copy_substring(pctx, str, substr_name, &substr);
449 if (ret != EOK) {
450 DEBUG(5, ("substring '%s' does not exist\n", substr_name));
451 return ret;
452 }
453
454 errno = 0;
455 val = strtol(substr, &err_ptr, 10);
456 if (substr == '\0' || *err_ptr != '\0' || errno != 0) {
457 DEBUG(5, ("substring '%s' does not contain an integerexist\n",
458 substr));
459 talloc_free(substr);
460 return EINVAL;
461 }
462
463 *out = val;
464 talloc_free(substr);
465 return EOK;
466 }
467
468 /*
469 * Compiles a regular expression REGEXP and tries to match it against the
470 * string STR. Fills in structure _PCTX with info about matching.
471 *
472 * Returns EOK on no error, EFAULT on bad regexp, EINVAL when it cannot
473 * match the regexp.
474 */
475 static int matches_regexp(TALLOC_CTX *ctx,
476 struct time_rules_ctx *trctx,
477 const char *str,
478 enum timelib_rgx regex,
479 struct parse_ctx **_pctx)
480 {
481 int ret;
482 struct parse_ctx *pctx = NULL;
483
484 pctx = talloc_zero(ctx, struct parse_ctx);
485 CHECK_PTR(pctx);
486 pctx->ovec = talloc_array(pctx, int, OVEC_SIZE);
487 CHECK_PTR_JMP(pctx->ovec);
488 pctx->re = trctx->re[regex];
489
490 ret = pcre_exec(pctx->re, NULL, str, strlen(str), 0, PCRE_NOTEMPTY, pctx->ovec, OVEC_SIZE);
491 if (ret <= 0) {
492 DEBUG(8, ("string '%s' did *NOT* match regexp '%s'\n", str, lookup_table[regex]));
493 ret = EINVAL;
494 goto done;
495 }
496 DEBUG(8, ("string '%s' matched regexp '%s'\n", str, lookup_table[regex]));
497
498 pctx->matches = ret;
499 *_pctx = pctx;
500 return EOK;
501
502 done:
503 talloc_free(pctx);
504 return ret;
505 }
506
507 /*******************************************************************
508 * date/time helper functions *
509 *******************************************************************/
510
511 /*
512 * Returns week number as an integer
513 * This may seem ugly, but I think it's actually less error prone
514 * than writing my own routine
515 */
516 static int weeknum(const struct tm *t)
517 {
518 char buf[3];
519
520 if (!strftime(buf, 3, "%U", t)) {
521 return -1;
522 }
523
524 /* %U returns 0-53, we want 1-54 */
525 return atoi(buf)+1;
526 }
527
528 /*
529 * Return the week of the month
530 * Range is 1 to 5
531 */
532 static int get_week_of_month(const struct tm *t)
533 {
534 int fs; /* first sunday */
535
536 fs = (t->tm_mday % 7) - t->tm_wday;
537 if (fs <= 0) {
538 fs += 7;
539 }
540
541 return (t->tm_mday <= fs) ? 1 : (2 + (t->tm_mday - fs - 1) / 7);
542 }
543
544 /*
545 * Normalize differencies between our HBAC definition and semantics of
546 * struct tm
547 */
548 static void abs2tm(struct tm *t)
549 {
550 /* tm defines tm_year as num of yrs since 1900, we have absolute number */
551 t->tm_year %= 1900;
552 /* struct tm defines tm_mon as number of month since January */
553 t->tm_mon--;
554 }
555
556 /*
557 * Normalize differencies between our HBAC definition and semantics of
558 * struct tm
559 */
560 static void tm2abs(struct tm *t)
561 {
562 /* tm defines tm_year as num of yrs since 1900, we have absolute number */
563 t->tm_year += 1900;
564 /* struct tm defines tm_mon as number of month since January */
565 t->tm_mon++;
566 }
567
568 /*******************************************************************
569 * parsing of HBAC rules themselves *
570 *******************************************************************/
571
572 /*
573 * Parses generalized time string given in STR and fills the
574 * information into OUT.
575 */
576 static int parse_generalized_time(struct parse_ctx *pctx,
577 struct time_rules_ctx *trctx,
578 const char *str,
579 time_t *out)
580 {
581 int ret;
582 struct parse_ctx *gctx = NULL;
583 struct tm tm;
584
585 memset(&tm, 0, sizeof(tm));
586 tm.tm_isdst = -1;
587
588 ret = matches_regexp(pctx, trctx, str, LP_RGX_GENERALIZED, &gctx);
589 JMP_NEOK(ret);
590
591 /* compulsory */
592 ret = substring_strtol(gctx, str, "year", &tm.tm_year);
593 JMP_NEOK(ret);
594 ret = substring_strtol(gctx, str, "month", &tm.tm_mon);
595 JMP_NEOK(ret);
596 ret = substring_strtol(gctx, str, "day", &tm.tm_mday);
597 JMP_NEOK(ret);
598 /* optional */
599 ret = substring_strtol(gctx, str, "hour", &tm.tm_hour);
600 JMP_NEOK_LABEL(ret, enoent);
601 ret = substring_strtol(gctx, str, "minute", &tm.tm_min);
602 JMP_NEOK_LABEL(ret, enoent);
603 ret = substring_strtol(gctx, str, "second", &tm.tm_sec);
604 JMP_NEOK_LABEL(ret, enoent);
605
606 enoent:
607 if (ret == ENOENT) {
608 ret = EOK;
609 }
610
611 abs2tm(&tm);
612
613 *out = mktime(&tm);
614 DEBUG(3, ("converted to time: '%s'\n", ctime(out)));
615 if (*out == -1) {
616 ret = EINVAL;
617 }
618 done:
619 talloc_free(gctx);
620 return ret;
621 }
622
623 /*
624 * Parses absolute timerange string given in STR and fills the
625 * information into ABS.
626 */
627 static int parse_absolute(struct absolute_range *absr,
628 struct time_rules_ctx *trctx,
629 struct parse_ctx *pctx,
630 const char *str)
631 {
632 char *from = NULL, *to = NULL;
633 int ret;
634
635 ret = copy_substring(pctx, str, "from", &from);
636 if (ret != EOK) {
637 DEBUG(1, ("Missing required part 'from' in absolute timespec\n"));
638 ret = EINVAL;
639 goto done;
640 }
641 ret = copy_substring(pctx, str, "to", &to);
642 if (ret != EOK) {
643 DEBUG(1, ("Missing required part 'to' in absolute timespec\n"));
644 ret = EINVAL;
645 goto done;
646 }
647
648 ret = parse_generalized_time(pctx, trctx, from, &absr->time_from);
649 if (ret != EOK) {
650 DEBUG(1, ("Cannot parse generalized time - first part\n"));
651 goto done;
652 }
653
654 ret = parse_generalized_time(pctx, trctx, to, &absr->time_to);
655 if (ret != EOK) {
656 DEBUG(1, ("Cannot parse generalized time - second part\n"));
657 goto done;
658 }
659
660 if (difftime(absr->time_to, absr->time_from) < 0) {
661 DEBUG(1, ("Not a valid interval\n"));
662 ret = EINVAL;
663 }
664
665 ret = EOK;
666 done:
667 talloc_free(from);
668 talloc_free(to);
669 return ret;
670 }
671
672 static int parse_hhmm(const char *str, int *hour, int *min)
673 {
674 struct tm t;
675 char *err;
676
677 err = strptime(str, "%H%M", &t);
678 if (*err != '\0') {
679 return EINVAL;
680 }
681
682 *hour = t.tm_hour;
683 *min = t.tm_min;
684
685 return EOK;
686 }
687
688 /*
689 * Parses monthly periodic timerange given in STR.
690 * Fills the information into PER.
691 */
692 static int parse_periodic_monthly(TALLOC_CTX *ctx,
693 struct time_rules_ctx *trctx,
694 struct periodic_range *per,
695 const char *str)
696 {
697 int ret;
698 struct parse_ctx *mpctx = NULL;
699 char *match = NULL;
700 char *mperspec = NULL;
701
702 /* This code would be much less ugly if RHEL5 PCRE knew about PCRE_DUPNAMES */
703 ret = matches_regexp(ctx, trctx, str, LP_RGX_MDAY, &mpctx);
704 if (ret == EOK) {
705 ret = copy_substring(mpctx, str, "mperspec_day", &mperspec);
706 JMP_NEOK(ret);
707 ret = copy_substring(mpctx, str, "interval_day", &match);
708 JMP_NEOK(ret);
709 BUFFER_OR_JUMP(per, per->day_of_month, DAY_OF_MONTH_BUFSIZE);
710 ret = interval2bitfield(mpctx, per->day_of_month, match,
711 1, DAY_OF_MONTH_MAX, NULL);
712 JMP_NEOK(ret);
713 } else {
714 ret = matches_regexp(ctx, trctx, str, LP_RGX_MWEEK, &mpctx);
715 JMP_NEOK(ret);
716 ret = copy_substring(mpctx, str, "mperspec_week", &mperspec);
717 JMP_NEOK(ret);
718
719 ret = copy_substring(mpctx, str, "interval_week", &match);
720 JMP_NEOK(ret);
721 ret = interval2bitfield(mpctx, &per->week_of_month, match,
722 1, WEEK_OF_MONTH_MAX, NULL);
723 JMP_NEOK(ret);
724
725 ret = copy_substring(mpctx, str, "day_of_week", &match);
726 JMP_NEOK(ret);
727 ret = interval2bitfield(mpctx, &per->day_of_week, match,
728 1, DAY_OF_WEEK_MAX, names_day_of_week);
729 JMP_NEOK(ret);
730 }
731
732 done:
733 talloc_free(mpctx);
734 return ret;
735 }
736
737 /*
738 * Parses yearly periodic timerange given in STR.
739 * Fills the information into PER.
740 */
741 static int parse_periodic_yearly(TALLOC_CTX *ctx,
742 struct time_rules_ctx *trctx,
743 struct periodic_range *per,
744 const char *str)
745 {
746 int ret;
747 struct parse_ctx *ypctx = NULL;
748 char *match = NULL;
749 char *yperspec = NULL;
750
751 ret = matches_regexp(ctx, trctx, str, LP_RGX_YEARLY, &ypctx);
752 JMP_NEOK(ret);
753 ret = copy_substring(ypctx, str, "yperspec_day", &yperspec);
754 if (ret == EOK) {
755 ret = copy_substring(ypctx, str, "day_of_year", &match);
756 JMP_NEOK(ret);
757 BUFFER_OR_JUMP(per, per->day_of_year, DAY_OF_YEAR_BUFSIZE);
758 ret = interval2bitfield(ypctx, per->day_of_year, match,
759 1, DAY_OF_YEAR_MAX, NULL);
760 JMP_NEOK(ret);
761 }
762
763 if (ret != ENOENT) goto done;
764
765 ret = copy_substring(ypctx, str, "yperspec_week", &yperspec);
766 if (ret == EOK) {
767 ret = copy_substring(ypctx, str, "week_of_year", &match);
768 JMP_NEOK(ret);
769 BUFFER_OR_JUMP(per, per->week_of_year, WEEK_OF_YEAR_BUFSIZE);
770 ret = interval2bitfield(ypctx, per->week_of_year, match,
771 1, WEEK_OF_YEAR_MAX, NULL);
772 JMP_NEOK(ret);
773
774 talloc_free(match);
775 ret = copy_substring(ypctx, str, "day_of_week", &match);
776 JMP_NEOK(ret);
777 ret = interval2bitfield(ypctx, &per->day_of_week, match,
778 1, DAY_OF_WEEK_MAX, names_day_of_week);
779 JMP_NEOK(ret);
780 }
781
782 if (ret != ENOENT) goto done;
783
784 ret = copy_substring(ypctx, str, "yperspec_month", &yperspec);
785 JMP_NEOK(ret);
786
787 talloc_free(match);
788 ret = copy_substring(ypctx, str, "month_number", &match);
789 JMP_NEOK(ret);
790 BUFFER_OR_JUMP(per, per->month, MONTH_BUFSIZE);
791 ret = interval2bitfield(ypctx, per->month, match,
792 1, MONTH_MAX, names_months);
793 JMP_NEOK(ret);
794
795 talloc_free(match);
796 ret = copy_substring(ypctx, str, "m_period", &match);
797 JMP_NEOK(ret);
798 DEBUG(7, ("Monthly year period - calling parse_periodic_monthly()\n"));
799 ret = parse_periodic_monthly(ypctx, trctx, per, match);
800 JMP_NEOK(ret);
801
802 done:
803 talloc_free(ypctx);
804 return ret;
805 }
806
807 /*
808 * Parses weekly periodic timerange given in STR.
809 * Fills the information into PER.
810 */
811 static int parse_periodic_weekly(TALLOC_CTX *ctx,
812 struct time_rules_ctx *trctx,
813 struct periodic_range *per,
814 const char *str)
815 {
816 int ret;
817 struct parse_ctx *wpctx = NULL;
818 char *dow = NULL;
819
820 ret = matches_regexp(ctx, trctx, str, LP_RGX_WEEKLY, &wpctx);
821 JMP_NEOK(ret);
822
823 ret = copy_substring(wpctx, str, "day_of_week", &dow);
824 JMP_NEOK(ret);
825 DEBUG(8, ("day_of_week = '%s'\n", dow));
826
827 ret = interval2bitfield(wpctx, &per->day_of_week, dow,
828 1, DAY_OF_WEEK_MAX, names_day_of_week);
829
830 done:
831 talloc_free(wpctx);
832 return ret;
833 }
834
835 static int parse_periodic_time(struct periodic_range *per,
836 struct parse_ctx *pctx,
837 const char *str)
838 {
839 char *substr = NULL;
840 int ret;
841
842 int hour_from;
843 int hour_to;
844 int min_from;
845 int min_to;
846
847 /* parse out the time */
848 ret = copy_substring(pctx, str, "timeFrom", &substr);
849 JMP_NEOK(ret);
850 parse_hhmm(substr, &hour_from, &min_from);
851 DEBUG(7, ("Parsed timeFrom: %d:%d\n", hour_from, min_from));
852 JMP_NEOK(ret);
853
854 talloc_free(substr);
855 ret = copy_substring(pctx, str, "timeTo", &substr);
856 JMP_NEOK(ret);
857 parse_hhmm(substr, &hour_to, &min_to);
858 DEBUG(7, ("Parsed timeTo: %d:%d\n", hour_to, min_to));
859 JMP_NEOK(ret);
860
861 /* set the interval */
862 if (hour_from > hour_to ) {
863 set_bit_range(per->hour, 0, hour_to);
864 set_bit_range(per->hour, hour_from, HOUR_MAX);
865 } else {
866 set_bit_range(per->hour, hour_from, hour_to);
867 }
868
869 if (min_from > min_to) {
870 set_bit_range(per->minute, 0, min_to);
871 set_bit_range(per->minute, min_from, MINUTE_MAX);
872 } else {
873 set_bit_range(per->minute, min_from, min_to);
874 }
875
876
877 ret = EOK;
878 done:
879 talloc_free(substr);
880 return ret;
881 }
882
883 /*
884 * Parses periodic timerange given in STR.
885 * Fills the information into PER.
886 */
887 static int parse_periodic(struct periodic_range *per,
888 struct time_rules_ctx *trctx,
889 struct parse_ctx *pctx,
890 const char *str)
891 {
892 char *substr = NULL;
893 char *period = NULL;
894 int ret;
895
896 /* These are mandatory */
897 BUFFER_OR_JUMP(per, per->hour, HOUR_BUFSIZE);
898 BUFFER_OR_JUMP(per, per->minute, MINUTE_BUFSIZE);
899
900 ret = copy_substring(pctx, str, "perspec", &substr);
901 JMP_NEOK(ret);
902 ret = copy_substring(pctx, str, "period", &period);
903 JMP_NEOK(ret);
904
905 if (strcmp(substr, "yearly") == 0) {
906 DEBUG(5, ("periodic yearly\n"));
907 ret = parse_periodic_yearly(pctx, trctx, per, period);
908 JMP_NEOK(ret);
909 } else if (strcmp(substr, "monthly") == 0) {
910 DEBUG(5, ("periodic monthly\n"));
911 ret = parse_periodic_monthly(pctx, trctx, per, period);
912 JMP_NEOK(ret);
913 } else if (strcmp(substr, "weekly") == 0) {
914 DEBUG(5, ("periodic weekly\n"));
915 ret = parse_periodic_weekly(pctx, trctx, per, period);
916 JMP_NEOK(ret);
917 } else if (strcmp(substr, "daily") == 0) {
918 DEBUG(5, ("periodic daily\n"));
919 } else {
920 DEBUG(1, ("Cannot determine periodic rule type"
921 "(perspec = '%s', period = '%s')\n", substr, period));
922 ret = EINVAL;
923 goto done;
924 }
925
926 talloc_free(period);
927
928 ret = parse_periodic_time(per, pctx, str);
929 JMP_NEOK(ret);
930
931 ret = EOK;
932 done:
933 talloc_free(substr);
934 return ret;
935 }
936
937 /*
938 * Parses time specification given in string RULE into range_ctx
939 * context CTX.
940 */
941 static int parse_timespec(struct range_ctx *ctx, const char *rule)
942 {
943 int ret;
944 struct parse_ctx *pctx = NULL;
945
946 if (matches_regexp(ctx, ctx->trctx, rule, LP_RGX_ABSOLUTE, &pctx) == EOK) {
947 DEBUG(5, ("Matched absolute range\n"));
948 ctx->type = TYPE_ABSOLUTE;
949 ctx->abs = talloc_zero(ctx, struct absolute_range);
950 CHECK_PTR_JMP(ctx->abs);
951
952 ret = parse_absolute(ctx->abs, ctx->trctx, pctx, rule);
953 JMP_NEOK(ret);
954 } else if (matches_regexp(ctx, ctx->trctx, rule, LP_RGX_PERIODIC, &pctx) == EOK) {
955 DEBUG(5, ("Matched periodic range\n"));
956 ctx->type = TYPE_PERIODIC;
957 ctx->per = talloc_zero(ctx, struct periodic_range);
958 CHECK_PTR_JMP(ctx->per);
959
960 ret = parse_periodic(ctx->per, ctx->trctx, pctx, rule);
961 JMP_NEOK(ret);
962 } else {
963 DEBUG(1, ("Cannot determine rule type\n"));
964 ret = EINVAL;
965 goto done;
966 }
967
968 ret = EOK;
969 done:
970 talloc_free(pctx);
971 return ret;
972 }
973
974 /*******************************************************************
975 * validation of rules against time_t *
976 *******************************************************************/
977
978 static int absolute_timerange_valid(struct absolute_range *absr,
979 const time_t now,
980 bool *result)
981 {
982 if (difftime(absr->time_from, now) > 0) {
983 DEBUG(3, ("Absolute timerange invalid (before interval)\n"));
984 *result = false;
985 return EOK;
986 }
987
988 if (difftime(absr->time_to, now) < 0) {
989 DEBUG(3, ("Absolute timerange invalid (after interval)\n"));
990 *result = false;
991 return EOK;
992 }
993
994 DEBUG(3, ("Absolute timerange valid\n"));
995 *result = true;
996 return EOK;
997 }
998
999 static int periodic_timerange_valid(struct periodic_range *per,
1000 const time_t now,
1001 bool *result)
1002 {
1003 struct tm tm_now;
1004 int wnum;
1005 int wom;
1006
1007 memset(&tm_now, 0, sizeof(struct tm));
At conditional (1): "localtime_r(&now, &tm_now) == NULL": Taking false branch.
|
1008 if (localtime_r(&now, &tm_now) == NULL) {
1009 DEBUG(0, ("Cannot convert time_t to struct tm\n"));
1010 return EFAULT;
1011 }
At conditional (2): "9 <= debug_level": Taking true branch.
At conditional (3): "debug_timestamps": Taking true branch.
| |
1012 DEBUG(9, ("Got struct tm value %s", asctime(&tm_now)));
1013 tm2abs(&tm_now);
1014
1015 wnum = weeknum(&tm_now);
At conditional (4): "wnum == -1": Taking false branch.
|
1016 if (wnum == -1) {
1017 DEBUG(7, ("Cannot get week number"));
1018 return EINVAL;
1019 }
At conditional (5): "9 <= debug_level": Taking true branch.
At conditional (6): "debug_timestamps": Taking true branch.
| |
1020 DEBUG(9, ("Week number is %d\n", wnum));
1021
1022 wom = get_week_of_month(&tm_now);
At conditional (7): "wnum == -1": Taking false branch.
|
1023 if (wnum == -1) {
1024 DEBUG(7, ("Cannot get week of number"));
1025 return EINVAL;
1026 }
At conditional (8): "9 <= debug_level": Taking true branch.
At conditional (9): "debug_timestamps": Taking true branch.
| |
1027 DEBUG(9, ("Week of month number is %d\n", wom));
1028
1029 /* The validation itself */
Event address_of: Taking address with "&per->day_of_week" yields a singleton pointer. Event callee_ptr_arith: Passing "&per->day_of_week" to function "test_bit" which uses it as an array. This might corrupt or misinterpret adjacent memory locations. [model]
At conditional (10): "per->day_of_week": Taking true branch.
| |
1030 TEST_BIT_RANGE(per->day_of_week, tm_now.tm_wday, result);
1031 DEBUG(9, ("day of week OK\n"));
1032 TEST_BIT_RANGE_PTR(per->day_of_month, tm_now.tm_mday, result);
1033 DEBUG(9, ("day of month OK\n"));
1034 TEST_BIT_RANGE(per->week_of_month, wom, result);
1035 DEBUG(9, ("week of month OK\n"));
1036 TEST_BIT_RANGE_PTR(per->week_of_year, wnum, result);
1037 DEBUG(9, ("week of year OK\n"));
1038 TEST_BIT_RANGE_PTR(per->month, tm_now.tm_mon, result);
1039 DEBUG(9, ("month OK\n"));
1040 TEST_BIT_RANGE_PTR(per->day_of_year, tm_now.tm_yday, result);
1041 DEBUG(9, ("day of year OK\n"));
1042 TEST_BIT_RANGE_PTR(per->hour, tm_now.tm_hour, result);
1043 DEBUG(9, ("hour OK\n"));
1044 TEST_BIT_RANGE_PTR(per->minute, tm_now.tm_min, result);
1045 DEBUG(9, ("minute OK\n"));
1046
1047 DEBUG(3, ("Periodic timerange valid\n"));
1048 *result = true;
1049 return EOK;
1050 }
1051
1052 /*
1053 * Returns EOK if the timerange in range_ctx context is valid compared against a
1054 * given time_t value in NOW, returns ERANGE if the time value is outside the
1055 * specified range.
1056 */
1057 static int timerange_valid(struct range_ctx *ctx,
1058 const time_t now,
1059 bool *result)
1060 {
1061 int ret;
1062
1063 switch(ctx->type) {
1064 case TYPE_ABSOLUTE:
1065 DEBUG(7, ("Checking absolute range\n"));
1066 ret = absolute_timerange_valid(ctx->abs, now, result);
1067 break;
1068
1069 case TYPE_PERIODIC:
1070 DEBUG(7, ("Checking periodic range\n"));
1071 ret = periodic_timerange_valid(ctx->per, now, result);
1072 break;
1073
1074 default:
1075 DEBUG(1, ("Unknown range type (%d)\n", ctx->type));
1076 ret = EINVAL;
1077 break;
1078 }
1079
1080 return ret;
1081 }
1082
1083 /*******************************************************************
1084 * public interface *
1085 *******************************************************************/
1086
1087 /*
1088 * This is actually the meat of the library. The function takes a string
1089 * representation of a time rule in STR and time to check against (usually that
1090 * would be current time) in NOW.
1091 *
1092 * It returns EOK if the rule is valid in the current time, ERANGE if not and
1093 * EINVAL if the rule cannot be parsed
1094 */
1095 int check_time_rule(TALLOC_CTX *mem_ctx,
1096 struct time_rules_ctx *trctx,
1097 const char *str,
1098 const time_t now,
1099 bool *result)
1100 {
1101 int ret;
1102 struct range_ctx *ctx;
1103
1104 ctx = talloc_zero(mem_ctx, struct range_ctx);
1105 CHECK_PTR_JMP(ctx);
1106 ctx->trctx = trctx;
1107
1108 DEBUG(9, ("Got time_t value %s", ctime(&now)));
1109
1110 ret = parse_timespec(ctx, str);
1111 if (ret != EOK) {
1112 DEBUG(1, ("Cannot parse the time specification (%d)\n", ret));
1113 goto done;
1114 }
1115
1116 ret = timerange_valid(ctx, now, result);
1117 if (ret != EOK) {
1118 DEBUG(1, ("Cannot check the time range (%d)\n", ret));
1119 goto done;
1120 }
1121
1122 ret = EOK;
1123 done:
1124 talloc_free(ctx);
1125 return EOK;
1126 }
1127
1128 /*
1129 * Frees the resources taken by the precompiled rules
1130 */
1131 static int time_rules_parser_destructor(struct time_rules_ctx *ctx)
1132 {
1133 int i;
1134
1135 for (i = 0; i< LP_RGX_MAX; ++i) {
1136 pcre_free(ctx->re[i]);
1137 ctx->re[i] = NULL;
1138 }
1139
1140 return 0;
1141 }
1142
1143 /*
1144 * Initializes the parser by precompiling the regular expressions
1145 * for later use
1146 */
1147 int init_time_rules_parser(TALLOC_CTX *mem_ctx,
1148 struct time_rules_ctx **_out)
1149 {
1150 const char *errstr;
1151 int errval;
1152 int errpos;
1153 int ret;
1154 int i;
1155 struct time_rules_ctx *ctx = NULL;
1156
1157 ctx = talloc_zero(mem_ctx, struct time_rules_ctx);
1158 CHECK_PTR(ctx);
1159 talloc_set_destructor(ctx, time_rules_parser_destructor);
1160
1161 /* Precompile regular expressions */
1162 for (i = LP_RGX_GENERALIZED; i< LP_RGX_MAX; ++i) {
1163 ctx->re[i] = pcre_compile2(lookup_table[i],
1164 0,
1165 &errval,
1166 &errstr,
1167 &errpos,
1168 NULL);
1169
1170 if (ctx->re[i] == NULL) {
1171 DEBUG(0, ("Invalid Regular Expression pattern '%s' at position %d"
1172 " (Error: %d [%s])\n", lookup_table[i],
1173 errpos, errval, errstr));
1174 ret = EFAULT;
1175 goto done;
1176 }
1177
1178 }
1179
1180 *_out = ctx;
1181 return EOK;
1182 done:
1183 talloc_free(ctx);
1184 return ret;
1185 }
1186