/
githubmirror
/
sssd
Обзор
Документация
Войти
/
githubmirror
/
sssd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/util/sss_ini.c
886 строк
24 KB
Alexey Tikhonov
INI: use proper deallocators
03 фев 2026, 15:34
03 фев 2026, 15:34
ade61ef
Код
Авторство
О чём код?
/* SSSD sss_ini.c Authors: Ondrej Kos <okos@redhat.com> Copyright (C) 2013 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 <stdio.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #include <talloc.h> #include <ini_configobj.h> #include "config.h" #include "util/util.h" #include "util/sss_ini.h" #include "confdb/confdb_setup.h" #include "confdb/confdb_private.h" struct sss_ini { char **error_list; struct ref_array *ra_success_list; struct ref_array *ra_error_list; struct ini_cfgobj *sssd_config; struct value_obj *obj; struct ini_cfgfile *file; bool main_config_exists; }; static void sss_ini_free_error_messages(struct sss_ini *self) { if (self != NULL) { ini_config_free_errors(self->error_list); self->error_list = NULL; } } static void sss_ini_free_ra_messages(struct sss_ini *self) { if (self != NULL) { ref_array_destroy(self->ra_success_list); self->ra_success_list = NULL; ref_array_destroy(self->ra_error_list); self->ra_error_list = NULL; } } static void sss_ini_free_config(struct sss_ini *self) { if (self != NULL && self->sssd_config != NULL) { ini_config_destroy(self->sssd_config); self->sssd_config = NULL; } } /* Close file descriptor */ static void sss_ini_close_file(struct sss_ini *self) { if (self != NULL && self->file != NULL) { ini_config_file_destroy(self->file); self->file = NULL; } } /* sss_ini destructor */ static int sss_ini_destroy(struct sss_ini *self) { sss_ini_free_error_messages(self); sss_ini_free_ra_messages(self); sss_ini_free_config(self); sss_ini_close_file(self); return 0; } /* Initialize data structure */ struct sss_ini* sss_ini_new(TALLOC_CTX *mem_ctx) { struct sss_ini *self; self = talloc_zero(mem_ctx, struct sss_ini); if (!self) { DEBUG(SSSDBG_CRIT_FAILURE, "Not enough memory for sss_ini_data.\n"); return NULL; } talloc_set_destructor(self, sss_ini_destroy); return self; } /* Open configuration file */ static int sss_ini_config_file_open(struct sss_ini *self, const char *config_file) { int ret; if (self == NULL) { return EINVAL; } ret = ini_config_file_open(config_file, INI_META_STATS, &self->file); self->main_config_exists = (ret != ENOENT); return ret; } static int sss_ini_config_file_from_mem(struct sss_ini *self, void *data_buf, uint32_t data_len) { if (self == NULL) { return EINVAL; } return ini_config_file_from_mem(data_buf, strlen(data_buf), &self->file); } /* Print ini_config errors */ static void sss_ini_config_print_errors(char **error_list) { unsigned count = 0; if (!error_list) { return; } while (error_list[count]) { DEBUG(SSSDBG_FATAL_FAILURE, "%s\n", error_list[count]); count++; } } static int sss_ini_parse(struct sss_ini *self) { int ret; if (!self) { return EINVAL; } sss_ini_free_error_messages(self); sss_ini_free_config(self); /* Create config object */ ret = ini_config_create(&(self->sssd_config)); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to create config object. Error %d.\n", ret); return ret; } /* Parse file */ ret = ini_config_parse(self->file, INI_STOP_ON_ANY, INI_MV1S_OVERWRITE, INI_PARSE_NOWRAP, self->sssd_config); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse configuration. Error %d.\n", ret); if (ini_config_error_count(self->sssd_config)) { DEBUG(SSSDBG_FATAL_FAILURE, "Errors detected while parsing: %s\n", ini_config_get_filename(self->file)); ini_config_get_errors(self->sssd_config, &(self->error_list)); } } return ret; } static int sss_ini_add_snippets(struct sss_ini *self, const char *config_dir) { int ret; const char *patterns[] = { "^[^\\.].*\\.conf$", NULL }; const char *sections[] = { ".*", NULL }; uint32_t i = 0; char *msg = NULL; struct ini_cfgobj *modified_sssd_config = NULL; if (self == NULL || self->sssd_config == NULL || config_dir == NULL) { return EINVAL; } sss_ini_free_ra_messages(self); ret = ini_config_augment(self->sssd_config, config_dir, patterns, sections, NULL, INI_STOP_ON_ANY, INI_MV1S_OVERWRITE, INI_PARSE_NOWRAP, INI_MV2S_OVERWRITE, &modified_sssd_config, &self->ra_error_list, &self->ra_success_list); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to augment configuration [%d]: %s\n", ret, sss_strerror(ret)); } while (ref_array_get(self->ra_error_list, i, &msg) != NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Config merge error: %s\n", msg); i++; } /* switch config objects if there are no errors */ if (modified_sssd_config != NULL) { ini_config_destroy(self->sssd_config); self->sssd_config = modified_sssd_config; } else { DEBUG(SSSDBG_TRACE_FUNC, "Using only main configuration file due to errors in merging\n"); } return ret; } struct ref_array * sss_ini_get_ra_success_list(struct sss_ini *self) { return self->ra_success_list; } struct ref_array * sss_ini_get_ra_error_list(struct sss_ini *self) { return self->ra_error_list; } /* Get configuration object */ int sss_ini_get_cfgobj(struct sss_ini *self, const char *section, const char *name) { return ini_get_config_valueobj(section,name, self->sssd_config, INI_GET_FIRST_VALUE, &self->obj); } /* Check configuration object */ int sss_ini_check_config_obj(struct sss_ini *self) { if (self->obj == NULL) { return ENOENT; } return EOK; } /* Get integer value */ int sss_ini_get_int_config_value(struct sss_ini *self, int strict, int def, int *error) { return ini_get_int_config_value(self->obj, strict, def, error); } /* Get string value */ char *sss_ini_get_string_config_value(struct sss_ini *self, int *error) { return ini_get_string_config_value(self->obj, error); } /* Create LDIF */ int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx, const struct sss_ini *self, const char *only_section, const char **config_ldif) { int ret, i, j; char *ldif = NULL; char *tmp_ldif; char **sections; int section_count; char *dn; char *tmp_dn; char *sec_dn; char **attrs; int attr_count; char *ldif_attr; TALLOC_CTX *tmp_ctx; size_t dn_size; size_t ldif_len = 0; size_t attr_len; struct value_obj *obj = NULL; tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) { ret = ENOMEM; goto error; } /* Read in the collection and convert it to an LDIF */ /* Get the list of sections */ sections = ini_get_section_list(self->sssd_config, §ion_count, &ret); if (ret != EOK) { goto error; } for (i = 0; i < section_count; i++) { const char *rdn = NULL; DEBUG(SSSDBG_TRACE_LDB, "Processing config section [%s]\n", sections[i]); ret = parse_section(tmp_ctx, sections[i], &sec_dn, &rdn); if (ret != EOK) { goto error; } if (only_section != NULL) { if (strcasecmp(only_section, sections[i])) { DEBUG(SSSDBG_TRACE_LDB, "Skipping section %s\n", sections[i]); continue; } } dn = talloc_asprintf(tmp_ctx, "dn: %s,cn=config\n" "cn: %s\n", sec_dn, rdn); if (!dn) { ret = ENOMEM; ini_free_section_list(sections); goto error; } dn_size = strlen(dn); /* Get all of the attributes and their values as LDIF */ attrs = ini_get_attribute_list(self->sssd_config, sections[i], &attr_count, &ret); if (ret != EOK) { ini_free_section_list(sections); goto error; } for (j = 0; j < attr_count; j++) { DEBUG(SSSDBG_TRACE_LDB, "Processing attribute [%s]\n", attrs[j]); ret = ini_get_config_valueobj(sections[i], attrs[j], self->sssd_config, INI_GET_FIRST_VALUE, &obj); if (ret != EOK) goto error; const char *value = ini_get_const_string_config_value(obj, &ret); if (ret != EOK) goto error; if (value && value[0] == '\0') { DEBUG(SSSDBG_CRIT_FAILURE, "Attribute '%s' has empty value, ignoring\n", attrs[j]); continue; } ldif_attr = talloc_asprintf(tmp_ctx, "%s: %s\n", attrs[j], value); DEBUG(SSSDBG_TRACE_LDB, "%s\n", ldif_attr); attr_len = strlen(ldif_attr); tmp_dn = talloc_realloc(tmp_ctx, dn, char, dn_size+attr_len+1); if (!tmp_dn) { ret = ENOMEM; ini_free_attribute_list(attrs); ini_free_section_list(sections); goto error; } dn = tmp_dn; memcpy(dn+dn_size, ldif_attr, attr_len+1); dn_size += attr_len; } dn_size ++; tmp_dn = talloc_realloc(tmp_ctx, dn, char, dn_size+1); if (!tmp_dn) { ret = ENOMEM; ini_free_attribute_list(attrs); ini_free_section_list(sections); goto error; } dn = tmp_dn; dn[dn_size-1] = '\n'; dn[dn_size] = '\0'; DEBUG(SSSDBG_TRACE_LDB, "Section dn\n%s\n", dn); tmp_ldif = talloc_realloc(mem_ctx, ldif, char, ldif_len+dn_size+1); if (!tmp_ldif) { ret = ENOMEM; ini_free_attribute_list(attrs); ini_free_section_list(sections); goto error; } ldif = tmp_ldif; memcpy(ldif+ldif_len, dn, dn_size); ldif_len += dn_size; ini_free_attribute_list(attrs); talloc_free(dn); } if (ldif == NULL) { ret = ERR_INI_EMPTY_CONFIG; goto error; } ldif[ldif_len] = '\0'; ini_free_section_list(sections); *config_ldif = (const char *)ldif; talloc_free(tmp_ctx); return EOK; error: talloc_free(ldif); talloc_free(tmp_ctx); return ret; } static errno_t check_domain_inherit_from(char *cfg_section, struct ini_cfgobj *config_obj, struct ini_errobj *errobj) { struct value_obj *vo = NULL; int ret; ret = ini_get_config_valueobj(cfg_section, "inherit_from", config_obj, INI_GET_NEXT_VALUE, &vo); if (ret != EOK) { goto done; } if (vo != NULL) { ret = ini_errobj_add_msg(errobj, "Attribute 'inherit_from' is not " "allowed in section '%s'.", cfg_section); if (ret != EOK) { goto done; } } ret = EOK; done: return ret; } static errno_t check_domain_id_provider(char *cfg_section, struct ini_cfgobj *config_obj, struct ini_errobj *errobj) { struct value_obj *vo = NULL; const char *valid_values[] = { "ad", "ipa", "ldap", "proxy", NULL }; const char **valid_value; const char *value; int ret; ret = ini_get_config_valueobj(cfg_section, "id_provider", config_obj, INI_GET_NEXT_VALUE, &vo); if (ret != EOK) { goto done; } if (vo == NULL) { ret = ini_errobj_add_msg(errobj, "Attribute 'id_provider' is " "missing in section '%s'.", cfg_section); } else { value = ini_get_const_string_config_value(vo, &ret); if (ret != EOK) { goto done; } valid_value = valid_values; while (*valid_value != NULL) { if (strcmp(value, *valid_value) == 0) { break; } valid_value++; } if (*valid_value == NULL) { ret = ini_errobj_add_msg(errobj, "Attribute 'id_provider' in section '%s' " "has an invalid value: %s", cfg_section, value); if (ret != EOK) { goto done; } } } ret = EOK; done: return ret; } #define SECTION_IS_DOMAIN(s) \ (strncmp("domain/", s, strlen("domain/")) == 0) #define SECTION_DOMAIN_IS_SUBDOMAIN(s) \ (strchr(s + strlen("domain/"), '/') != NULL) /* Here we can put custom SSSD specific checks that can not be implemented * using libini validators */ static int custom_sssd_checks(const char *rule_name, struct ini_cfgobj *rules_obj, struct ini_cfgobj *config_obj, struct ini_errobj *errobj, void **data) { char **cfg_sections = NULL; int num_cfg_sections; int ret; /* Get all sections in configuration */ cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret); if (ret != EOK) { goto done; } /* Check a normal domain section (not application domains) */ for (int i = 0; i < num_cfg_sections; i++) { if (SECTION_IS_DOMAIN(cfg_sections[i])) { ret = check_domain_inherit_from(cfg_sections[i], config_obj, errobj); if (ret != EOK) { goto done; } if (!SECTION_DOMAIN_IS_SUBDOMAIN(cfg_sections[i])) { ret = check_domain_id_provider(cfg_sections[i], config_obj, errobj); if (ret != EOK) { goto done; } } } } ret = EOK; done: ini_free_section_list(cfg_sections); return ret; } static int sss_ini_call_validators_errobj(struct sss_ini *data, const char *rules_path, struct ini_errobj *errobj) { int ret; struct ini_cfgobj *rules_cfgobj = NULL; struct ini_validator custom_sssd = { "sssd_checks", custom_sssd_checks, NULL }; struct ini_validator *sss_validators[] = { &custom_sssd, NULL }; ret = ini_rules_read_from_file(rules_path, &rules_cfgobj); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to read sssd.conf schema [%d]: %s\n", ret, strerror(ret)); goto done; } ret = ini_rules_check(rules_cfgobj, data->sssd_config, sss_validators, errobj); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "ini_rules_check failed [%d]: %s\n", ret, strerror(ret)); goto done; } done: if (rules_cfgobj) ini_config_destroy(rules_cfgobj); return ret; } int sss_ini_call_validators(struct sss_ini *data, const char *rules_path) { int ret; struct ini_errobj *errobj = NULL; ret = ini_errobj_create(&errobj); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to create error list\n"); goto done; } ret = sss_ini_call_validators_errobj(data, rules_path, errobj); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to get errors from validators.\n"); goto done; } /* Do not error out when validators find some issue */ while (!ini_errobj_no_more_msgs(errobj)) { DEBUG(SSSDBG_CRIT_FAILURE, "%s\n", ini_errobj_get_msg(errobj)); ini_errobj_next(errobj); } ret = EOK; done: ini_errobj_destroy(&errobj); return ret; } int sss_ini_call_validators_strs(TALLOC_CTX *mem_ctx, struct sss_ini *data, const char *rules_path, char ***_errors, size_t *_num_errors) { TALLOC_CTX *tmp_ctx = NULL; struct ini_errobj *errobj = NULL; int ret; size_t num_errors; char **errors = NULL; if (_num_errors == NULL || _errors == NULL) { return EINVAL; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { return ENOMEM; } ret = ini_errobj_create(&errobj); if (ret != EOK) { goto done; } ret = sss_ini_call_validators_errobj(data, rules_path, errobj); if (ret != EOK) { goto done; } num_errors = ini_errobj_count(errobj); if (num_errors == 0) { *_num_errors = num_errors; goto done; } errors = talloc_array(tmp_ctx, char *, num_errors); if (errors == NULL) { ret = ENOMEM; goto done; } for (int i = 0; i < num_errors; i++) { errors[i] = talloc_strdup(errors, ini_errobj_get_msg(errobj)); if (errors[i] == NULL) { ret = ENOMEM; goto done; } ini_errobj_next(errobj); } *_num_errors = num_errors; *_errors = talloc_steal(mem_ctx, errors); ret = EOK; done: talloc_free(tmp_ctx); ini_errobj_destroy(&errobj); return ret; } int sss_ini_open(struct sss_ini *self, const char *config_file, const char *fallback_cfg) { int ret; if (self == NULL) { return EINVAL; } if (config_file != NULL) { ret = sss_ini_config_file_open(self, config_file); } else { ret = ENOENT; } switch (ret) { case EOK: break; case ENOENT: DEBUG(SSSDBG_TRACE_FUNC, "No %s.\n", config_file); if (fallback_cfg == NULL) { return ret; } ret = sss_ini_config_file_from_mem(self, discard_const(fallback_cfg), strlen(fallback_cfg)); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "sss_ini_config_file_from_mem() failed [%d]: %s\n", ret, sss_strerror(ret)); } break; default: DEBUG(SSSDBG_CONF_SETTINGS, "sss_ini_config_file_open() failed [%d]: %s\n", ret, sss_strerror(ret)); sss_ini_config_print_errors(self->error_list); break; } return ret; } static int access_check_file(const char *filename) { int ret; struct stat st; uid_t uid; gid_t gid; sss_sssd_user_uid_and_gid(&uid, &gid); ret = stat(filename, &st); if (ret != 0) { ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, "stat(%s) failed: %s\n", filename, strerror(ret)); return EINVAL; } if ((st.st_uid != 0) && (st.st_uid != uid)) { DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected user owner of '%s': %"SPRIuid"\n", filename, st.st_uid); return ERR_INI_INVALID_PERMISSION; } if ((st.st_gid != 0) && (st.st_gid != gid)) { DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected group owner of '%s': %"SPRIgid"\n", filename, st.st_gid); return ERR_INI_INVALID_PERMISSION; } if ((st.st_mode & (S_IROTH|S_IWOTH|S_IXOTH)) != 0) { DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected access to '%s' by other users\n", filename); return ERR_INI_INVALID_PERMISSION; } return EOK; } static int access_check_ini(struct sss_ini *self) { int ret; const char *path; uint32_t i; const char **snippet; struct ref_array *used_snippets; if (self->main_config_exists) { path = ini_config_get_filename(self->file); ret = access_check_file(path); if (ret != EOK) { return ret; } } used_snippets = sss_ini_get_ra_success_list(self); for (i = 0; (snippet = ref_array_get(used_snippets, i, NULL)) != NULL; ++i) { ret = access_check_file(*snippet); if (ret != EOK) { return ret; } } return EOK; } int sss_ini_read_sssd_conf(struct sss_ini *self, const char *config_file, const char *config_dir) { errno_t ret; if (self == NULL) { return EINVAL; } /* "[sssd]\n" is supplied to `sss_ini_open()` to create empty context * in case main config file ('sssd.conf') is missing. This is done in * order to be able to add config snippets later - sss_ini_add_snippets() * Take a note if both 'sssd.conf' and snippets are missing, then * sss_ini_read_sssd_conf() returns ERR_INI_EMPTY_CONFIG, so there is no * "fallback config" per se. */ ret = sss_ini_open(self, config_file, "[sssd]\n"); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "sss_ini_open() on '%s' failed [%d]: %s\n", config_file, ret, sss_strerror(ret)); return ERR_INI_OPEN_FAILED; } if (!self->main_config_exists) { DEBUG(SSSDBG_CONF_SETTINGS, "File %s does not exist.\n", config_file); } ret = sss_ini_parse(self); if (ret != EOK) { sss_ini_config_print_errors(self->error_list); DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse configuration file %s: %d\n", config_file, ret); return ERR_INI_PARSE_FAILED; } ret = sss_ini_add_snippets(self, config_dir); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Error while reading configuration directory '%s' [%d]: %s\n", config_dir, ret, sss_strerror(ret)); return ERR_INI_ADD_SNIPPETS_FAILED; } if ((!self->main_config_exists) && (ref_array_len(sss_ini_get_ra_success_list(self)) == 0)) { return ERR_INI_EMPTY_CONFIG; } ret = access_check_ini(self); return ret; }