From 2863e720288ca8e985bc9e26c0b735e6ea1e4ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=BDidek?= Date: Fri, 15 Apr 2016 12:13:58 +0200 Subject: [PATCH 4/8] ini: Add internal validator allowed_sections Ticket: https://fedorahosted.org/sssd/ticket/133 This validator allows to list all sections that can be used in config file. Sections can be specified using string or regular expression. Format is: [rule/allowed_sections] validator = ini_allowed_sections section = foo section = bar section_re = ^baz/.* Signed-off-by: Lukas Slebodnik --- ini/ini_configobj.c | 238 +++++++++++++++++++++++++++++++++++++++++- ini/ini_validators_ut_check.c | 222 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 451 insertions(+), 9 deletions(-) diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c index e40bb4ad2ab10c0e4180bf838920c4d5f279a81e..287377df2177368b8442f6fd259e9ea27ba4c53c 100644 --- a/ini/ini_configobj.c +++ b/ini/ini_configobj.c @@ -1096,6 +1096,239 @@ static int ini_dummy_error(const char *rule_name, return ini_errobj_add_msg(errobj, "Error"); } +static int is_allowed_section(const char *tested_section, + char **allowed_sections, + size_t num_sec, + regex_t *allowed_sections_re, + size_t num_sec_re, + int case_insensitive) +{ + int ret; + int i; + + if (case_insensitive) { + for (i = 0; i < num_sec; i++) { + if (strcasecmp(tested_section, allowed_sections[i]) == 0) { + return 1; + } + } + } else { /* case insensitive */ + for (i = 0; i < num_sec; i++) { + if (strcmp(tested_section, allowed_sections[i]) == 0) { + return 1; + } + } + } + + for (i = 0; i < num_sec_re; i++) { + ret = regexec(&allowed_sections_re[i], tested_section, 0, NULL, 0); + if (ret == 0) { + return 1; + } + } + + return 0; +} + +static int ini_allowed_sections(const char *rule_name, + struct ini_cfgobj *rules_obj, + struct ini_cfgobj *config_obj, + struct ini_errobj *errobj) +{ + struct value_obj *vo = NULL; + int ret; + char *regex_str = NULL; + char **allowed_sections = NULL; + char *insensitive_str; + char **cfg_sections = NULL; + int num_cfg_sections; + char **attributes = NULL; + int num_attributes; + size_t num_sec = 0; + size_t num_sec_re = 0; + regex_t *allowed_sections_re = NULL; + size_t buf_size; + int reg_err; + int is_allowed; + int case_insensitive = 0; + int regcomp_flags = REG_NOSUB; + int i; + + /* Get number of 'section' and 'section_re' attributes + * in this rule */ + attributes = ini_get_attribute_list(rules_obj, + rule_name, + &num_attributes, + NULL); + if (attributes == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < num_attributes; i++) { + if (strcmp("section", attributes[i]) == 0) { + num_sec++; + } + + if (strcmp("section_re", attributes[i]) == 0) { + num_sec_re++; + } + } + + ini_free_attribute_list(attributes); + + if (num_sec == 0 && num_sec_re == 0) { + /* This rule is empty. */ + ret = ini_errobj_add_msg(errobj, + "No allowed sections specified. " + "Use 'section = default' to allow only " + "default section"); + goto done; + } + + ret = ini_get_config_valueobj(rule_name, + "case_insensitive", + rules_obj, + INI_GET_NEXT_VALUE, + &vo); + if (ret) { + goto done; + } + + if (vo) { + insensitive_str = ini_get_string_config_value(vo, &ret); + if (ret) { + goto done; + } + + if (strcasecmp(insensitive_str, "yes") == 0 + || strcasecmp(insensitive_str, "true") == 0 + || strcmp(insensitive_str, "1") == 0) { + case_insensitive = 1; + regcomp_flags |= REG_ICASE; + } + + free(insensitive_str); + } + + /* Create arrays for section_re regexes and section name + * strings. */ + allowed_sections = calloc(num_sec + 1, sizeof(char *)); + if (allowed_sections == NULL) { + ret = ENOMEM; + goto done; + } + + allowed_sections_re = calloc(num_sec_re + 1, sizeof(regex_t)); + if (allowed_sections_re == NULL) { + ret = ENOMEM; + goto done; + } + + /* Get all allowed section names and store them to + * allowed_sections array */ + for (i = 0; i < num_sec; i++) { + ret = ini_get_config_valueobj(rule_name, + "section", + rules_obj, + INI_GET_NEXT_VALUE, + &vo); + if (ret) { + goto done; + } + + allowed_sections[i] = ini_get_string_config_value(vo, &ret); + if (ret) { + goto done; + } + } + + /* Get all regular section_re regular expresions and + * store them to allowed_sections_re array */ + for (i = 0; i < num_sec_re; i++) { + ret = ini_get_config_valueobj(rule_name, + "section_re", + rules_obj, + INI_GET_NEXT_VALUE, + &vo); + if (ret) { + goto done; + } + + regex_str = ini_get_string_config_value(vo, &ret); + if (ret) { + goto done; + } + + reg_err = regcomp(&allowed_sections_re[i], regex_str, regcomp_flags); + if (reg_err) { + char *err_str; + + buf_size = regerror(reg_err, &allowed_sections_re[i], NULL, 0); + err_str = malloc(buf_size); + if (err_str == NULL) { + ret = ENOMEM; + goto done; + } + + regerror(reg_err, &allowed_sections_re[i], err_str, buf_size); + ret = ini_errobj_add_msg(errobj, + "Validator failed to use regex [%s]:[%s]", + regex_str, err_str); + free(err_str); + ret = ret ? ret : EINVAL; + goto done; + } + free(regex_str); + regex_str = NULL; + } + + /* Finally get list of all sections in configuration and + * check if they are matched by some string in allowed_sections + * or regex in allowed_sections_re */ + cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret); + if (ret != EOK) { + goto done; + } + + for (i = 0; i < num_cfg_sections; i++) { + is_allowed = is_allowed_section(cfg_sections[i], + allowed_sections, + num_sec, + allowed_sections_re, + num_sec_re, + case_insensitive); + if (!is_allowed) { + ret = ini_errobj_add_msg(errobj, + "Section [%s] is not allowed. " + "Check for typos.", + cfg_sections[i]); + if (ret) { + goto done; + } + } + } + + ret = EOK; +done: + if (allowed_sections != NULL) { + for (i = 0; allowed_sections[i] != NULL; i++) { + free(allowed_sections[i]); + } + free(allowed_sections); + } + if (allowed_sections_re != NULL) { + for (i = 0; i < num_sec_re; i++) { + regfree(&allowed_sections_re[i]); + } + free(allowed_sections_re); + } + ini_free_section_list(cfg_sections); + free(regex_str); + + return ret; +} + static int check_if_allowed(char *section, char *attr, char **allowed, int num_allowed, struct ini_errobj *errobj) { @@ -1185,7 +1418,8 @@ static int ini_allowed_options(const char *rule_name, regerror(reg_err, &preg, err_str, buf_size); ret = ini_errobj_add_msg(errobj, - "Validator misses 'section_re' parameter"); + "Cannot compile regular expression from " + "option 'section_re'. Error: '%s'", err_str); ret = ret ? ret : EINVAL; goto done; } @@ -1293,6 +1527,8 @@ get_validator(char *validator_name, return ini_dummy_error; } else if (strcmp(validator_name, "ini_allowed_options") == 0) { return ini_allowed_options; + } else if (strcmp(validator_name, "ini_allowed_sections") == 0) { + return ini_allowed_sections; } /* Now check the custom validators */ diff --git a/ini/ini_validators_ut_check.c b/ini/ini_validators_ut_check.c index 2a6bbb9f606026001234597eaffc6d9b378c1d0d..78dc7d6245d1096e55b77f9af0a186ad26b039cd 100644 --- a/ini/ini_validators_ut_check.c +++ b/ini/ini_validators_ut_check.c @@ -502,14 +502,6 @@ START_TEST(test_ini_allowed_sections_str_ok) ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); - /* validator is not implemented yet. */ - ret = strcmp("Rule 'rule/section_list' uses unknown validator " - "'ini_allowed_sections'.", - ini_errobj_get_msg(errobj)); - fail_unless(ret == 0, - "Unexpected errors found: [%s]", ini_errobj_get_msg(errobj)); - ini_errobj_next(errobj); - /* Should generate 0 errors */ fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found: [%s]", ini_errobj_get_msg(errobj)); @@ -520,6 +512,213 @@ START_TEST(test_ini_allowed_sections_str_ok) } END_TEST +START_TEST(test_ini_allowed_sections_str_typos) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int num_err; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section = foo\n" + "section = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[fooo]\n" + "br = 0\n" + "bra = 0\n" + "[baar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors */ + fail_if(ini_errobj_no_more_msgs(errobj), + "Expected 2 errors but none found"); + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_str_insensitive) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "case_insensitive = yes\n" + "section = foo\n" + "section = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[FOo]\n" + "br = 0\n" + "bra = 0\n" + "[baR]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found"); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_re_ok) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section_re = ^foo*$\n" + "section_re = bar\n"; + + char input_cfg[] = + "[foooooooooooo]\n" + "br = 0\n" + "bra = 0\n" + "[my_bar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found"); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_re_typos) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int num_err; + int ret; + + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "section_re = ^foo*$\n" + "section_re = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[fooooooOooooo]\n" + "br = 0\n" + "bra = 0\n" + "[my_bra]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 2 errors */ + fail_if(ini_errobj_no_more_msgs(errobj), + "Expected 2 errors but none found"); + num_err = ini_errobj_count(errobj); + fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + +START_TEST(test_ini_allowed_sections_re_insensitive) +{ + struct ini_cfgobj *rules_obj; + struct ini_cfgobj *cfg_obj; + struct ini_errobj *errobj; + int ret; + + /* Only bar and baz are allowed for foo section */ + char input_rules[] = + "[rule/section_list]\n" + "validator = ini_allowed_sections\n" + "case_insensitive = yes\n" + "section_re = ^foo*$\n" + "section_re = bar\n"; + + /* Make 4 typos */ + char input_cfg[] = + "[FOoOoOoOoOOOOooo]\n" + "br = 0\n" + "bra = 0\n" + "[my_Bar]\n" + "abz = 0\n"; + + create_rules_from_str(input_rules, &rules_obj); + cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg)); + + ret = ini_errobj_create(&errobj); + fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret)); + + ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj); + fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret)); + + /* Should generate 0 errors */ + fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found"); + + ini_errobj_destroy(&errobj); + ini_config_destroy(cfg_obj); + ini_rules_destroy(rules_obj); +} +END_TEST + static Suite *ini_validators_utils_suite(void) { Suite *s = suite_create("ini_validators"); @@ -539,6 +738,13 @@ static Suite *ini_validators_utils_suite(void) TCase *tc_allowed_sections = tcase_create("ini_allowed_sections"); tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_str_ok); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_str_typos); + tcase_add_test(tc_allowed_sections, + test_ini_allowed_sections_str_insensitive); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_re_ok); + tcase_add_test(tc_allowed_sections, test_ini_allowed_sections_re_typos); + tcase_add_test(tc_allowed_sections, + test_ini_allowed_sections_re_insensitive); suite_add_tcase(s, tc_infrastructure); suite_add_tcase(s, tc_allowed_options); -- 2.7.4