[SSSD] Time-Based Policies in FreeIPA

Dmitri Pal dpal at redhat.com
Tue Jul 14 18:24:26 UTC 2015


On 07/14/2015 04:50 AM, Sumit Bose wrote:
> On Tue, Jul 14, 2015 at 09:11:28AM +0200, Stanislav Laznicka wrote:
>> Hi,
>>
>> If you're a member of the FreeIPA-devel mailing list, you may already know
>> me. I am currently trying to re-introduce the time-based policies for
>> FreeIPA HBAC Rules to both FreeIPA and SSSD. For some more information, see
>> the design at http://www.freeipa.org/page/V4/Time-Based_Account_Policies or
>> read the whole paper at
>> http://www.fit.vutbr.cz/study/DP/DP.php.cs?id=17185&file=t.
>>
>> At the moment, I have a prototype solution which is able to decide whether
>> an HBAC rule should or should not apply based on the time rules supplied
>> with HBAC rule objects from FreeIPA. See the source code attached to this
>> mail, if you will. The biggest problem of this solution is that it is not
>> thread-safe.
> I have not look in detain at you patches but I think thread safety is
> not an issue here. The HBAC evaluation is done in a single process
> without using threads. To handle concurrent task we use the tevent
> library and asynchronous request (see
> https://tevent.samba.org/tevent_request.html for details). This means
> whatever you do will change the state of the whole process but as long
> as you do it in a single function and make sure to restore the original
> state it might be safe. I say it might because tevent handles signals as
> well so we have to make sure the state can be recovered even if a single
> handler runs in between.
>
> bye,
> Sumit
>
>> It seems to be quite a problem to convert time from a certain time zone to a
>> different time zone in C and keep it thread-safe. A very simple and also
>> very ugly solution would be to have a mutex to guard each localtime() call
>> as well as it should wrap the body of the time_to_timezone() function from
>> the second patch. This seems rather unacceptable. The other solution would
>> be to find another way to convert the time. Currently, there seems to be a
>> C++ Boost solution based on a .csv file but it is not accepted well (https://github.com/boostorg/date_time/blob/master/data/date_time_zonespec.csv).
>> I was also thinking on using the glibc tzfile parsers
>> (http://code.woboq.org/userspace/glibc/time/tzfile.c.html#__tzfile_read) but
>> they too seem rather thread-unsafe and trying to rework it in a thread-safe
>> manner might be a painful thing to do.
>>
>> I welcome any suggestions and ideas on the topic as I seem to be quite stuck
>> here.
>>
>> Cheers,
>> Stanislav Laznicka
>>  From 428db72ca0a07c7dba1a6959696b3dc2df7d77ec Mon Sep 17 00:00:00 2001
>> From: Stanislav Laznicka <slaznick at redhat.com>
>> Date: Mon, 13 Jul 2015 09:53:10 +0200
>> Subject: [PATCH 1/2] Added caching of time policies for IPA HBAC rules.
>>
>> The time policies which are part of the FreeIPA HBAC Rule object
>> are now being cached along with other HBAC Rule objects' attributes.
>> Also, the cached time policies are transformed into hbac_time_rules
>> structure representation.
>>
>> https://fedorahosted.org/freeipa/ticket/547
>> https://fedorahosted.org/freeipa/ticket/548
>> ---
>>   Makefile.am                          |   4 --
>>   src/providers/ipa/hbac_evaluator.c   |   7 --
>>   src/providers/ipa/ipa_access.c       |   3 +
>>   src/providers/ipa/ipa_hbac.h         |   6 +-
>>   src/providers/ipa/ipa_hbac_common.c  | 120 +++++++++++++++++++++++++++++++++++
>>   src/providers/ipa/ipa_hbac_private.h |  10 +++
>>   src/providers/ipa/ipa_hbac_rules.c   |   7 +-
>>   src/tests/ipa_hbac-tests.c           |   6 ++
>>   8 files changed, 149 insertions(+), 14 deletions(-)
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index b8cbc6df23ded1edb945a709b6dbe1c44eb54017..8665a0560cc3cd57f148325640c2e709a05f4c2a 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>> @@ -264,14 +264,10 @@ PYTHON_TESTS =
>>   
>>   if BUILD_PYTHON2_BINDINGS
>>   PYTHON_TESTS += src/config/SSSDConfigTest.py2.sh \
>> -                src/tests/pyhbac-test.py2.sh \
>> -                src/tests/pysss_murmur-test.py2.sh \
>>                   $(NULL)
>>   endif
>>   if BUILD_PYTHON3_BINDINGS
>>   PYTHON_TESTS += src/config/SSSDConfigTest.py3.sh \
>> -                src/tests/pyhbac-test.py3.sh \
>> -                src/tests/pysss_murmur-test.py3.sh \
>>                   $(NULL)
>>   endif
>>   
>> diff --git a/src/providers/ipa/hbac_evaluator.c b/src/providers/ipa/hbac_evaluator.c
>> index f40f9e0a7f16f5e012079c637b89c8e49ec5d15b..64af36aae2855d0fb5a737989cc3ee8959e1b5b6 100644
>> --- a/src/providers/ipa/hbac_evaluator.c
>> +++ b/src/providers/ipa/hbac_evaluator.c
>> @@ -38,13 +38,6 @@ typedef int errno_t;
>>   #define EOK 0
>>   #endif
>>   
>> -/* Placeholder structure for future HBAC time-based
>> - * evaluation rules
>> - */
>> -struct hbac_time_rules {
>> -    int not_yet_implemented;
>> -};
>> -
>>   enum hbac_eval_result_int {
>>       HBAC_EVAL_MATCH_ERROR = -1,
>>       HBAC_EVAL_MATCHED,
>> diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
>> index 3198e2bd2a4c8355eeccc129c85ae3d7d67f61b0..c60775fd01f816c19b3617caa04088a8052d35ed 100644
>> --- a/src/providers/ipa/ipa_access.c
>> +++ b/src/providers/ipa/ipa_access.c
>> @@ -686,6 +686,9 @@ errno_t hbac_get_cached_rules(TALLOC_CTX *mem_ctx,
>>                               IPA_EXTERNAL_HOST,
>>                               IPA_MEMBER_HOST,
>>                               IPA_HOST_CATEGORY,
>> +                            IPA_TIMEZONE,
>> +                            IPA_ACCESSTIME,
>> +                            IPA_ACCESSTIME_EXCLUDE,
>>                               NULL };
>>   
>>       tmp_ctx = talloc_new(NULL);
>> diff --git a/src/providers/ipa/ipa_hbac.h b/src/providers/ipa/ipa_hbac.h
>> index f43611351c8a5dfb20ca3d075f0bcd7bb71798c9..7f8d4ecf78aced49c5523f0487269f03aa9e6569 100644
>> --- a/src/providers/ipa/ipa_hbac.h
>> +++ b/src/providers/ipa/ipa_hbac.h
>> @@ -73,7 +73,11 @@ enum hbac_eval_result {
>>   /**
>>    * Opaque type contained in hbac_evaluator.c
>>    */
>> -struct hbac_time_rules;
>> + struct hbac_time_rules {
>> +     const char *timezone;
>> +     const char **accesstimes;
>> +     const char **exceptions;
>> + };
>>   
>>   /**
>>    * Component of an HBAC rule
>> diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c
>> index 72a620ef0971a8bc657bd7bda3f61b4abdd614ee..4d9e5a1d0e135ec057608b1e748ea1c1183645a3 100644
>> --- a/src/providers/ipa/ipa_hbac_common.c
>> +++ b/src/providers/ipa/ipa_hbac_common.c
>> @@ -354,6 +354,16 @@ hbac_attrs_to_rule(TALLOC_CTX *mem_ctx,
>>           goto done;
>>       }
>>   
>> +    ret = hbac_time_attrs_to_rule(new_rule, new_rule->name,
>> +                                  hbac_ctx->rules[idx],
>> +                                  &new_rule->timerules);
>> +    if (ret != EOK) {
>> +        DEBUG(SSSDBG_CRIT_FAILURE,
>> +              "Could not parse time rules for rule [%s]\n",
>> +                new_rule->name);
>> +        goto done;
>> +    }
>> +
>>       *rule = new_rule;
>>       ret = EOK;
>>   
>> @@ -363,6 +373,116 @@ done:
>>   }
>>   
>>   errno_t
>> +hbac_time_attrs_to_rule(TALLOC_CTX *mem_ctx,
>> +                        const char *rule_name,
>> +                        struct sysdb_attrs *rule_attrs,
>> +                        struct hbac_time_rules **times)
>> +{
>> +    errno_t ret;
>> +    TALLOC_CTX *tmp_ctx = NULL;
>> +    struct ldb_message_element *el;
>> +    struct hbac_time_rules *new_times = NULL;
>> +    int i;
>> +
>> +    tmp_ctx = talloc_new(mem_ctx);
>> +    if (tmp_ctx == NULL) return ENOMEM;
>> +
>> +    new_times = talloc_zero(tmp_ctx, struct hbac_time_rules);
>> +    if (new_times == NULL) {
>> +        ret = ENOMEM;
>> +        goto done;
>> +    }
>> +
>> +    DEBUG(SSSDBG_TRACE_LIBS, "Processing time policies for rule [%s]\n",
>> +            rule_name);
>> +
>> +    ret = sysdb_attrs_get_el(rule_attrs, IPA_TIMEZONE, &el);
>> +    if (ret != EOK && ret != ENOENT) {
>> +        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_el failed.\n");
>> +        goto done;
>> +    }
>> +    if (ret == ENOENT || el->num_values == 0) {
>> +        /* No value = default behavior (utc) */
>> +        el->num_values = 0;
>> +        new_times->timezone = talloc_strdup(new_times, "utc");
>> +    }
>> +    else {
>> +        new_times->timezone = talloc_strndup(new_times,
>> +                                             (const char*) el->values[0].data,
>> +                                             el->values[0].length);
>> +    }
>> +
>> +    ret = sysdb_attrs_get_el(rule_attrs, IPA_ACCESSTIME, &el);
>> +    if(ret != EOK && ret != ENOENT) {
>> +        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_el failed");
>> +        goto done;
>> +    }
>> +    if(ret == ENOENT || el->num_values == 0) {
>> +        el->num_values = 0;
>> +        DEBUG(SSSDBG_CONF_SETTINGS,
>> +              "No access time specified.\n");
>> +    }
>> +
>> +    new_times->accesstimes = talloc_array(new_times,
>> +                                          const char *,
>> +                                          el->num_values + 1);
>> +    if (new_times->accesstimes == NULL) {
>> +        ret = ENOMEM;
>> +        goto done;
>> +    }
>> +
>> +    for(i = 0; i < el->num_values; i++) {
>> +        new_times->accesstimes[i] = talloc_strdup(new_times->accesstimes,
>> +                                        (const char *)el->values[i].data);
>> +        if(new_times->accesstimes[i] == NULL) {
>> +            ret = ENOMEM;
>> +            goto done;
>> +        }
>> +    }
>> +    new_times->accesstimes[i] = NULL;
>> +
>> +    ret = sysdb_attrs_get_el(rule_attrs, IPA_ACCESSTIME_EXCLUDE, &el);
>> +    if(ret != EOK && ret != ENOENT) {
>> +        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_el failed");
>> +        goto done;
>> +    }
>> +    if(ret == ENOENT || el->num_values == 0) {
>> +        el->num_values = 0;
>> +        DEBUG(SSSDBG_CONF_SETTINGS,
>> +              "No access time exceptions specified.\n");
>> +    }
>> +
>> +    new_times->exceptions = talloc_array(new_times,
>> +                                          const char *,
>> +                                          el->num_values + 1);
>> +    if (new_times->exceptions == NULL) {
>> +        ret = ENOMEM;
>> +        goto done;
>> +    }
>> +
>> +    for(i = 0; i < el->num_values; i++) {
>> +        new_times->exceptions[i] = talloc_strdup(new_times->exceptions,
>> +                                        (const char *)el->values[i].data);
>> +        if(new_times->exceptions[i] == NULL) {
>> +            ret = ENOMEM;
>> +            goto done;
>> +        }
>> +    }
>> +    new_times->exceptions[i] = NULL;
>> +
>> +    ret = EOK;
>> +
>> +done:
>> +    if (ret == EOK) {
>> +        *times = talloc_steal(mem_ctx, new_times);
>> +    }
>> +    talloc_free(tmp_ctx);
>> +
>> +    return ret;
>> +}
>> +
>> +
>> +errno_t
>>   hbac_get_category(struct sysdb_attrs *attrs,
>>                     const char *category_attr,
>>                     uint32_t *_categories)
>> diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
>> index c831cd5c6dd2ed1ff2bc0d649a25ae1212548dda..2f009626569547202ae46105a5d2d4e4e62f36f4 100644
>> --- a/src/providers/ipa/ipa_hbac_private.h
>> +++ b/src/providers/ipa/ipa_hbac_private.h
>> @@ -53,6 +53,9 @@
>>   #define IPA_CN "cn"
>>   #define IPA_MEMBER_SERVICE "memberService"
>>   #define IPA_SERVICE_CATEGORY "serviceCategory"
>> +#define IPA_TIMEZONE "timezone"
>> +#define IPA_ACCESSTIME "accesstime"
>> +#define IPA_ACCESSTIME_EXCLUDE "accesstimeexclude"
>>   #define IPA_TRUE_VALUE "TRUE"
>>   
>>   #define IPA_HBAC_BASE_TMPL "cn=hbac,%s"
>> @@ -129,6 +132,13 @@ hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx,
>>                              const char *rule_name,
>>                              struct sysdb_attrs *rule_attrs,
>>                              struct hbac_rule_element **services);
>> +
>> +errno_t
>> +hbac_time_attrs_to_rule(TALLOC_CTX *mem_ctx,
>> +                       const char *rule_name,
>> +                       struct sysdb_attrs *rule_attrs,
>> +                       struct hbac_time_rules **times);
>> +
>>   errno_t
>>   get_ipa_servicegroupname(TALLOC_CTX *mem_ctx,
>>                            struct sysdb_ctx *sysdb,
>> diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
>> index ffef6dc4ce4229f2063d1b00308892bd3765f398..08dbaed5caa10b39ca7a8458335d57457ffc5255 100644
>> --- a/src/providers/ipa/ipa_hbac_rules.c
>> +++ b/src/providers/ipa/ipa_hbac_rules.c
>> @@ -94,7 +94,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
>>       state->opts = opts;
>>       state->search_bases = search_bases;
>>       state->search_base_iter = 0;
>> -    state->attrs = talloc_zero_array(state, const char *, 15);
>> +    state->attrs = talloc_zero_array(state, const char *, 18);
>>       if (state->attrs == NULL) {
>>           ret = ENOMEM;
>>           goto immediate;
>> @@ -113,7 +113,10 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
>>       state->attrs[11] = IPA_EXTERNAL_HOST;
>>       state->attrs[12] = IPA_MEMBER_HOST;
>>       state->attrs[13] = IPA_HOST_CATEGORY;
>> -    state->attrs[14] = NULL;
>> +    state->attrs[14] = IPA_TIMEZONE;
>> +    state->attrs[15] = IPA_ACCESSTIME;
>> +    state->attrs[16] = IPA_ACCESSTIME_EXCLUDE;
>> +    state->attrs[17] = NULL;
>>   
>>       rule_filter = talloc_asprintf(tmp_ctx,
>>                                     "(&(objectclass=%s)"
>> diff --git a/src/tests/ipa_hbac-tests.c b/src/tests/ipa_hbac-tests.c
>> index bd56c8f107b05f07b1ba8913fc14a03419d679f7..46f1e3e1e648a8a53f35857ba7752b81ef2425be 100644
>> --- a/src/tests/ipa_hbac-tests.c
>> +++ b/src/tests/ipa_hbac-tests.c
>> @@ -103,6 +103,12 @@ static void get_allow_all_rule(TALLOC_CTX *mem_ctx,
>>       rule->srchosts->names = NULL;
>>       rule->srchosts->groups = NULL;
>>   
>> +    rule->timerules = talloc_zero(rule, struct hbac_time_rules);
>> +    fail_if(rule->timerules == NULL);
>> +    rule->timerules->timezone = NULL;
>> +    rule->timerules->accesstimes = NULL;
>> +    rule->timerules->exceptions = NULL;
>> +
>>       *allow_rule = rule;
>>   }
>>   
>> -- 
>> 2.4.3
>>
>>  From 856dc04c7bd9b4f46637c7c598dfff0bb8b95105 Mon Sep 17 00:00:00 2001
>> From: Stanislav Laznicka <slaznick at redhat.com>
>> Date: Mon, 13 Jul 2015 10:00:42 +0200
>> Subject: [PATCH 2/2] Added evaluation of time-policies in HBAC objects.
>>
>> The time-policies in FreeIPA HBAC objects are now evaluated in
>> the libipa_hbac module.
>>
>> FIXME: The evaluation is not thread-safe. See the time_to_timezone
>> function in ipa_timerules.c
>>
>> https://fedorahosted.org/freeipa/ticket/547
>> https://fedorahosted.org/freeipa/ticket/548
>> ---
>>   Makefile.am                        |   2 +
>>   src/providers/ipa/hbac_evaluator.c |  17 +-
>>   src/providers/ipa/ipa_timerules.c  | 488 +++++++++++++++++++++++++++++++++++++
>>   src/providers/ipa/ipa_timerules.h  |  40 +++
>>   4 files changed, 545 insertions(+), 2 deletions(-)
>>   create mode 100644 src/providers/ipa/ipa_timerules.c
>>   create mode 100644 src/providers/ipa/ipa_timerules.h
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index 8665a0560cc3cd57f148325640c2e709a05f4c2a..937cc99757b501f953c91aef40a13e69fb4c4f4e 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>> @@ -600,6 +600,7 @@ dist_noinst_HEADERS = \
>>       src/providers/ldap/sdap_dyndns.h \
>>       src/providers/ldap/sdap_async_enum.h \
>>       src/providers/ipa/ipa_common.h \
>> +	src/providers/ipa/ipa_timerules.h \
>>       src/providers/ipa/ipa_config.h \
>>       src/providers/ipa/ipa_access.h \
>>       src/providers/ipa/ipa_selinux.h \
>> @@ -877,6 +878,7 @@ lib_LTLIBRARIES = libipa_hbac.la \
>>   pkgconfig_DATA += src/providers/ipa/ipa_hbac.pc
>>   libipa_hbac_la_DEPENDENCIES = src/providers/ipa/ipa_hbac.exports
>>   libipa_hbac_la_SOURCES = \
>> +	src/providers/ipa/ipa_timerules.c \
>>       src/providers/ipa/hbac_evaluator.c \
>>       src/util/sss_utf8.c
>>   libipa_hbac_la_LIBADD = \
>> diff --git a/src/providers/ipa/hbac_evaluator.c b/src/providers/ipa/hbac_evaluator.c
>> index 64af36aae2855d0fb5a737989cc3ee8959e1b5b6..5d9bdaf29e6352f7835ee316b00790f8a739fae3 100644
>> --- a/src/providers/ipa/hbac_evaluator.c
>> +++ b/src/providers/ipa/hbac_evaluator.c
>> @@ -27,6 +27,7 @@
>>   #include <string.h>
>>   #include <errno.h>
>>   #include "providers/ipa/ipa_hbac.h"
>> +#include "providers/ipa/ipa_timerules.h"
>>   #include "util/sss_utf8.h"
>>   
>>   #ifndef HAVE_ERRNO_T
>> @@ -106,7 +107,6 @@ enum hbac_eval_result hbac_evaluate(struct hbac_rule **rules,
>>       enum hbac_error_code ret;
>>       enum hbac_eval_result result = HBAC_EVAL_DENY;
>>       enum hbac_eval_result_int intermediate_result;
>> -
>>       if (info) {
>>           *info = malloc(sizeof(struct hbac_info));
>>           if (!*info) {
>> @@ -176,7 +176,8 @@ enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
>>       if (!rule->users
>>        || !rule->services
>>        || !rule->targethosts
>> -     || !rule->srchosts) {
>> +     || !rule->srchosts
>> +     || !rule->timerules) {
>>           *error = HBAC_ERROR_UNPARSEABLE_RULE;
>>           return HBAC_EVAL_MATCH_ERROR;
>>       }
>> @@ -224,6 +225,18 @@ enum hbac_eval_result_int hbac_evaluate_rule(struct hbac_rule *rule,
>>       } else if (!matched) {
>>           return HBAC_EVAL_UNMATCHED;
>>       }
>> +
>> +    /* Check time policies */
>> +    ret = hbac_evaluate_time_rules(rule->timerules,
>> +                                    hbac_req->request_time,
>> +                                    &matched);
>> +    if(ret != EOK) {
>> +        *error = HBAC_ERROR_UNPARSEABLE_RULE;
>> +        return HBAC_EVAL_MATCH_ERROR;
>> +    } else if (!matched) {
>> +        return HBAC_EVAL_UNMATCHED;
>> +    }
>> +
>>       return HBAC_EVAL_MATCHED;
>>   }
>>   
>> diff --git a/src/providers/ipa/ipa_timerules.c b/src/providers/ipa/ipa_timerules.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..9c406402c8c14429a8f67a8e0ff319908dc85aeb
>> --- /dev/null
>> +++ b/src/providers/ipa/ipa_timerules.c
>> @@ -0,0 +1,488 @@
>> +/*
>> +    SSSD
>> +
>> +    IPA Provider - Time Rules Parsing
>> +
>> +    Authors:
>> +        Stanislav Laznicka <slaz at seznam.cz>
>> +
>> +    Copyright (C) 2015 Red Hat
>> +
>> +    This program is free software; you can redistribute it and/or modify
>> +    it under the terms of the GNU General Public License as published by
>> +    the Free Software Foundation; either version 3 of the License, or
>> +    (at your option) any later version.
>> +
>> +    This program is distributed in the hope that it will be useful,
>> +    but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +    GNU General Public License for more details.
>> +
>> +    You should have received a copy of the GNU General Public License
>> +    along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +#include <errno.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include "util/sss_utf8.h"
>> +#include "providers/ipa/ipa_hbac_private.h"
>> +#include "providers/ipa/ipa_hbac.h"
>> +#include "providers/ipa/ipa_common.h"
>> +
>> +#define NORANGE -10001
>> +
>> +#define TOD_STR "timeofday"
>> +#define DOW_STR "dayofweek"
>> +#define DOM_STR "dayofmonth"
>> +#define WOM_STR "weekofmonth"
>> +#define MOY_STR "monthofyear"
>> +#define YEAR_STR "year"
>> +
>> +typedef int errno_t;
>> +
>> +enum time_types {
>> +    TOD = 0,
>> +    DOW,
>> +    DOM,
>> +    WOM,
>> +    MOY,
>> +    YEAR,
>> +    START,
>> +    END,
>> +    VAL_ERR,
>> +    MEM_ERR
>> +};
>> +
>> +struct time_token {
>> +    enum time_types type;
>> +    int value_low;
>> +    int value_high;
>> +    int pos;
>> +};
>> +
>> +errno_t
>> +eval_time_rule(const char *rule,
>> +                 struct tm* cmp_time,
>> +                 bool *matched);
>> +
>> +struct tm *time_to_timezone(time_t t, const char *tz);
>> +
>> +errno_t
>> +hbac_evaluate_time_rules(struct hbac_time_rules *rule,
>> +                         time_t req_time,
>> +                         bool *matched)
>> +{
>> +    errno_t ret;
>> +    struct tm *cmp_time;
>> +    int i;
>> +
>> +    /* No access time policies = always match */
>> +    if((!rule->accesstimes && !rule->exceptions)
>> +        || (!rule->accesstimes[0] && !rule->exceptions[0])) {
>> +        *matched = true;
>> +        return EOK;
>> +    }
>> +
>> +    if(!rule->timezone || strcasecmp(rule->timezone, "utc") == 0)
>> +        cmp_time = gmtime(&req_time);
>> +    else if(strcasecmp(rule->timezone, "host") == 0)
>> +        cmp_time = localtime(&req_time);
>> +    else cmp_time = time_to_timezone(req_time, rule->timezone);
>> +
>> +    if(cmp_time == NULL) return ENOMATCH;
>> +
>> +    /* conversion to our language */
>> +    if(cmp_time->tm_wday == 0)
>> +        cmp_time->tm_wday = 7;
>> +    cmp_time->tm_mon += 1;
>> +    cmp_time->tm_year += 1900;
>> +
>> +    if(rule->exceptions) {
>> +        for(i = 0; rule->exceptions[i]; i++) {
>> +            ret = eval_time_rule(rule->exceptions[i], cmp_time, matched);
>> +            if(ret != EOK) {
>> +                /*DEBUG(SSSDBG_CONF_SETTINGS,
>> +                      "Invalid value found in accessTimeExclude rule [%s]\n",
>> +                      rule->exceptions[i]);*/
>> +                *matched = false;
>> +                return ENOMATCH;
>> +            }
>> +            /* exception matched, rule can't be applied */
>> +            if(*matched == true) {
>> +                *matched = false;
>> +                return EOK;
>> +            }
>> +        }
>> +    }
>> +
>> +    if(!rule->accesstimes[0]) {
>> +        /* No match in exceptions and no accessTimes set -> the rule should apply */
>> +        *matched = true;
>> +        return EOK;
>> +    }
>> +
>> +    if(rule->accesstimes) {
>> +        for(i = 0; rule->accesstimes[i]; i++) {
>> +            ret = eval_time_rule(rule->accesstimes[i], cmp_time, matched);
>> +            if(ret != EOK) {
>> +                *matched = false;
>> +                return ENOMATCH;
>> +            }
>> +            else if(*matched == true) {
>> +                return EOK;
>> +            }
>> +        }
>> +    }
>> +    return ret;
>> +}
>> +
>> +struct tm *time_to_timezone(time_t t, const char *tz)
>> +{
>> +    struct tm *ret;
>> +    char *env_backup = NULL;
>> +    char *tz_toenv;
>> +    char *tmp = NULL;
>> +    int tzlen;
>> +
>> +    tzlen = strlen(tz);
>> +    /* + 2 because - prepending string with ':'; end is a null sign */
>> +    tz_toenv = malloc((tzlen + 2) * sizeof(char));
>> +    if(tz_toenv == NULL)
>> +        return NULL;
>> +    /* TZ variable is set */
>> +    if((tmp = getenv("TZ")) != NULL)
>> +        env_backup = strdup(tmp);
>> +
>> +    tz_toenv[0] = ':';
>> +    tz_toenv[tzlen+1] = '\0';
>> +    memcpy(&tz_toenv[1], tz, tzlen*sizeof(char));
>> +
>> +    if(setenv("TZ", tz_toenv, 1) != 0)
>> +        return NULL;
>> +
>> +    tzset();
>> +    ret = localtime(&t);
>> +
>> +    if(env_backup != NULL)
>> +        setenv("TZ", env_backup, 1);
>> +    else unsetenv("TZ");
>> +    free(tz_toenv);
>> +
>> +    return ret;
>> +}
>> +
>> +void get_token(const char *input, struct time_token *tok);
>> +errno_t check_range(int val, int low, int high);
>> +int get_week_of_month(struct tm *t);
>> +
>> +errno_t
>> +eval_time_rule(const char *rule,
>> +                 struct tm* cmp_time,
>> +                 bool *matched)
>> +{
>> +    struct time_token tok;
>> +    enum time_types prev;
>> +    errno_t ret;
>> +    int checked_val;
>> +
>> +    prev = START;
>> +    tok.type = START;
>> +    tok.pos = 0;
>> +    *matched = true;
>> +
>> +    do {
>> +        get_token(rule, &tok);
>> +
>> +        switch(tok.type) {
>> +            case TOD:
>> +                if((tok.value_low % 100) > 60 || tok.value_low > 2359)
>> +                    return EINVAL;
>> +
>> +                if(tok.value_high != NORANGE) {
>> +                    if((tok.value_high % 100) > 60 || tok.value_low > 2359)
>> +                        return EINVAL;
>> +                }
>> +
>> +                checked_val = cmp_time->tm_hour*100 + cmp_time->tm_min;
>> +
>> +                break;
>> +            case DOW:
>> +                if(tok.value_low > 7 || tok.value_high > 7)
>> +                    return EINVAL;
>> +
>> +                checked_val = cmp_time->tm_wday;
>> +
>> +                break;
>> +            case DOM:
>> +                if(tok.value_low > 31 || tok.value_high > 31)
>> +                    return EINVAL;
>> +                if(tok.value_low == 0 || tok.value_high == 0)
>> +                    return EINVAL;
>> +
>> +                checked_val = cmp_time->tm_mday;
>> +
>> +                break;
>> +            case WOM:
>> +                if(tok.value_low > 6 || tok.value_high > 6)
>> +                    return EINVAL;
>> +                if(tok.value_low == 0 || tok.value_high == 0)
>> +                    return EINVAL;
>> +
>> +                checked_val = get_week_of_month(cmp_time);
>> +
>> +                break;
>> +            case MOY:
>> +                if(tok.value_low > 12 || tok.value_high > 12)
>> +                    return EINVAL;
>> +                if(tok.value_low == 0 || tok.value_high == 0)
>> +                    return EINVAL;
>> +
>> +                checked_val = cmp_time->tm_mon;
>> +
>> +                break;
>> +            case YEAR:
>> +                if(tok.value_low < 1970)
>> +                    return EINVAL;
>> +                if(tok.value_high != NORANGE && tok.value_high < 1970)
>> +                    return EINVAL;
>> +
>> +                checked_val = cmp_time->tm_year;
>> +
>> +                break;
>> +            case END:
>> +                return EOK;
>> +            case MEM_ERR:
>> +                return ENOMEM;
>> +            default:
>> +                return EINVAL;
>> +        }
>> +
>> +        ret = check_range(checked_val, tok.value_low, tok.value_high);
>> +
>> +        /* invalid range */
>> +        if (ret == EINVAL)
>> +            return ret;
>> +
>> +        if(prev == tok.type) {
>> +            /* if val in range, set matched to true, else keep matched same */
>> +            if(ret == EOK)
>> +                *matched = true;
>> +        }
>> +        else {
>> +            /* a new attribute */
>> +            if(*matched == true) {
>> +                prev = tok.type;
>> +                *matched = (ret == EOK);
>> +            }
>> +            else {
>> +                /* previous attribute did not match */
>> +                *matched = false;
>> +                return EOK;
>> +            }
>> +        }
>> +
>> +    } while (tok.type < END);
>> +
>> +    return EOK;
>> +}
>> +
>> +int get_week_of_month(struct tm *t) {
>> +    int sun; /* first sunday */
>> +    int week = 1;
>> +
>> +    /* mod 7 turns mday into 0-6 interval, -curr_day gets a sunday */
>> +    sun = (t->tm_mday % 7) - t->tm_wday;
>> +    if(sun <= 0)
>> +        sun +=7;
>> +
>> +    if(t->tm_mday <= sun) return 1;
>> +    while(t->tm_mday > sun) {
>> +        sun+=7;
>> +        week++;
>> +    }
>> +    return week;
>> +}
>> +
>> +void get_token(const char *input, struct time_token *tok)
>> +{
>> +    int idx = tok->pos;
>> +    int cmplen;
>> +    char c;
>> +    char *part = NULL;
>> +    char *err = NULL;
>> +    int i;
>> +    int j;
>> +
>> +    while(((c = input[idx]) != '\0') && isspace(c))
>> +        idx++;
>> +
>> +    if(c != ',') {
>> +        switch(c) {
>> +            case 't':
>> +                cmplen = strlen(TOD_STR);
>> +                if(strncasecmp(&input[idx], TOD_STR, cmplen) != 0) {
>> +                    goto error;
>> +                }
>> +                tok->type=TOD;
>> +                idx += cmplen;
>> +                break;
>> +            case 'd':
>> +                cmplen = strlen(DOW_STR);
>> +                if(strncasecmp(&input[idx], DOW_STR, cmplen) != 0) {
>> +                    cmplen = strlen(DOM_STR);
>> +                    if(strncasecmp(&input[idx], DOM_STR, cmplen) != 0) {
>> +                        goto error;
>> +                    }
>> +                    tok->type = DOM;
>> +                    idx += cmplen;
>> +                    break;
>> +                }
>> +                tok->type = DOW;
>> +                idx += cmplen;
>> +                break;
>> +            case 'w':
>> +                cmplen = strlen(WOM_STR);
>> +                if(strncasecmp(&input[idx], WOM_STR, cmplen) != 0) {
>> +                    goto error;
>> +                }
>> +                tok->type = WOM;
>> +                idx += cmplen;
>> +                break;
>> +            case 'm':
>> +                cmplen = strlen(MOY_STR);
>> +                if(strncasecmp(&input[idx], MOY_STR, cmplen) != 0) {
>> +                    goto error;
>> +                }
>> +                tok->type = MOY;
>> +                idx += cmplen;
>> +                break;
>> +            case 'y':
>> +                cmplen = strlen(YEAR_STR);
>> +                if(strncasecmp(&input[idx], YEAR_STR, cmplen) != 0) {
>> +                    goto error;
>> +                }
>> +                tok->type = YEAR;
>> +                idx += cmplen;
>> +                break;
>> +            case '\0':
>> +                tok->type = END;
>> +                return;
>> +            default:
>> +                goto error;
>> +        } /* switch(c) */
>> +
>> +        while(((c = input[idx]) != '\0') && isspace(c))
>> +            idx++;
>> +
>> +        if(c != '=') {
>> +            goto error;
>> +        }
>> +    } /* if (c != ',') */
>> +    else {
>> +        if(tok->type == START) {
>> +            goto error;
>> +        }
>> +    }
>> +    idx++; /* either after '=' or ',' */
>> +
>> +    while(((c = input[idx]) != '\0') && isspace(c))
>> +        idx++;
>> +
>> +    /* 4 is the number of max chars to read in a number + 1 for \0 */
>> +    if((part = calloc(5, sizeof(char))) == NULL) {
>> +        tok->type = MEM_ERR;
>> +        return;
>> +    }
>> +
>> +    for(j = 0; j < 2; j++) {
>> +        switch(tok->type) {
>> +            case TOD:
>> +            case YEAR:
>> +                for(i = 0; i < 4; i++) {
>> +                    if(input[idx] == '\0') {
>> +                        goto error;
>> +                    }
>> +                    part[i] = input[idx];
>> +                    idx++;
>> +                }
>> +                break;
>> +            case DOW:
>> +            case WOM:
>> +                /* No need to check for \0, strtol will do that*/
>> +                part[0] = input[idx];
>> +                idx++;
>> +                break;
>> +            case DOM:
>> +            case MOY:
>> +                for(i = 0; i < 2; i++) {
>> +                    if(isdigit(input[idx])) {
>> +                        part[i] = input[idx];
>> +                        idx++;
>> +                    }
>> +                    else if(i == 0) /* not even a one-digit number */
>> +                        goto error;
>> +                }
>> +                break;
>> +            default:
>> +                goto error;
>> +        }
>> +
>> +        if(j == 0) {
>> +            tok->value_low = (int)strtol(part, &err, 10);
>> +            if(*err != '\0') {
>> +                goto error;
>> +            }
>> +            /* if it's not a range */
>> +            if(input[idx] != '-') {
>> +                tok->value_high = NORANGE;
>> +                free(part);
>> +                tok->pos = idx++;
>> +                return;
>> +            }
>> +            else idx++;
>> +        }
>> +        else {
>> +            tok->value_high = (int)strtol(part, &err, 10);
>> +            if(*err != '\0') {
>> +                goto error;
>> +            }
>> +        }
>> +    } /* endfor */
>> +
>> +    tok->pos = idx;
>> +    free(part);
>> +    return;
>> +
>> +error:
>> +    tok->type = VAL_ERR;
>> +    if(part != NULL)
>> +        free(part);
>> +    return;
>> +}
>> +
>> +errno_t check_range(int val, int low, int high)
>> +{
>> +    /* not a range */
>> +    if (high == NORANGE) {
>> +        if (val != low)
>> +            return ERANGE;
>> +        return EOK;
>> +    }
>> +    else if (high < 0) {
>> +        return EINVAL;
>> +    }
>> +
>> +    if(low < 0)
>> +        return EINVAL;
>> +
>> +    if (low > high)
>> +        return EINVAL;
>> +
>> +    if (val < low)
>> +        return ERANGE;
>> +
>> +    if (val > high)
>> +        return ERANGE;
>> +    return EOK;
>> +}
>> diff --git a/src/providers/ipa/ipa_timerules.h b/src/providers/ipa/ipa_timerules.h
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..291c48ec73d1745032edea653ed02d7eb43d4bbe
>> --- /dev/null
>> +++ b/src/providers/ipa/ipa_timerules.h
>> @@ -0,0 +1,40 @@
>> +/*
>> +    SSSD
>> +
>> +    IPA Provider - Time Rules Parsing
>> +
>> +    Authors:
>> +        Stanislav Laznicka <slaz at seznam.cz>
>> +
>> +    Copyright (C) 2015 Red Hat
>> +
>> +    This program is free software; you can redistribute it and/or modify
>> +    it under the terms of the GNU General Public License as published by
>> +    the Free Software Foundation; either version 3 of the License, or
>> +    (at your option) any later version.
>> +
>> +    This program is distributed in the hope that it will be useful,
>> +    but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +    GNU General Public License for more details.
>> +
>> +    You should have received a copy of the GNU General Public License
>> +    along with this program.  If not, see <http://www.gnu.org/licenses/>.
>> +*/
>> +
>> +#ifndef __IPA_TIMERULES_H_
>> +#define __IPA_TIMERULES_H_
>> +
>> +#include "providers/ipa/ipa_hbac.h"
>> +
>> +#ifndef HAVE_ERRNO_T
>> +#define HAVE_ERRNO_T
>> +typedef int errno_t;
>> +#endif
>> +
>> +errno_t
>> +hbac_evaluate_time_rules(struct hbac_time_rules *rule,
>> +                         time_t req_time,
>> +                         bool *matched);
>> +
>> +#endif /* __IPA_TIMERULES_H_ */
>> -- 
>> 2.4.3
>>
>> _______________________________________________
>> sssd-devel mailing list
>> sssd-devel at lists.fedorahosted.org
>> https://lists.fedorahosted.org/mailman/listinfo/sssd-devel
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

What about hbac test library?
AFAIU the same library is used on client - SSSD and server - HBAC test 
(via python) to process HBAC rules.
What are the expectations regarding the library? Is the code going 
inside the library? How this affects API?

-- 
Thank you,
Dmitri Pal

Director of Engineering for IdM portfolio
Red Hat, Inc.




More information about the sssd-devel mailing list