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 */
At conditional (10): "per->day_of_week": Taking true branch.
At conditional (11): "test_bit(&per->day_of_week, tm_now.tm_wday) == 0": Taking false branch.
1030 	    TEST_BIT_RANGE(per->day_of_week, tm_now.tm_wday, result);
At conditional (12): "9 <= debug_level": Taking true branch.
At conditional (13): "debug_timestamps": Taking true branch.
1031 	    DEBUG(9, ("day of week OK\n"));
At conditional (14): "per->day_of_month": Taking true branch.
At conditional (15): "test_bit(per->day_of_month, tm_now.tm_mday) == 0": Taking false branch.
1032 	    TEST_BIT_RANGE_PTR(per->day_of_month, tm_now.tm_mday, result);
At conditional (16): "9 <= debug_level": Taking true branch.
At conditional (17): "debug_timestamps": Taking true branch.
1033 	    DEBUG(9, ("day of month OK\n"));
Event address_of: Taking address with "&per->week_of_month" yields a singleton pointer.
Event callee_ptr_arith: Passing "&per->week_of_month" to function "test_bit" which uses it as an array. This might corrupt or misinterpret adjacent memory locations. [model]
At conditional (18): "per->week_of_month": Taking true branch.
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