[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