From: Todd C. Miller Date: Thu, 21 Feb 2019 00:05:02 +0000 (-0700) Subject: Add simple API for to allow reading environment data from different sources. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e1205f08d6ecb9ccd0be0687b47b5b510c034264;p=sudo Add simple API for to allow reading environment data from different sources. Currently, this is used to read a file like /etc/environment. --- diff --git a/plugins/sudoers/env.c b/plugins/sudoers/env.c index f908a3e59..1efdc6a9a 100644 --- a/plugins/sudoers/env.c +++ b/plugins/sudoers/env.c @@ -105,6 +105,25 @@ # define KEPT_USER_VARIABLES (KEPT_LOGNAME|KEPT_USER) #endif +/* + * Functions to open, close and parse an environment file, either + * a system file such as /etc/environment or one specified in sudoers. + */ +struct sudoers_env_file { + void * (*open)(const char *); + void (*close)(void *); + char * (*next)(void *, int *); +}; + +/* + * State for a local environment file. + */ +struct env_file_local { + FILE *fp; + char *line; + size_t linesize; +}; + struct environment { char **envp; /* pointer to the new environment */ char **old_envp; /* pointer the old environment we allocated */ @@ -1188,8 +1207,39 @@ validate_env_vars(char * const env_vars[]) debug_return_bool(ret); } +static void * +env_file_open_local(const char *path) +{ + struct env_file_local *efl; + debug_decl(env_file_open_local, SUDOERS_DEBUG_ENV) + + efl = calloc(1, sizeof(*efl)); + if (efl != NULL) { + if ((efl->fp = fopen(path, "r")) == NULL) { + free(efl); + efl = NULL; + } + } + debug_return_ptr(efl); +} + +static void +env_file_close_local(void *cookie) +{ + struct env_file_local *efl = cookie; + debug_decl(env_file_close_local, SUDOERS_DEBUG_ENV) + + if (efl != NULL) { + if (efl->fp != NULL) + fclose(efl->fp); + free(efl->line); + free(efl); + } + debug_return; +} + /* - * Read in /etc/environment ala AIX and Linux. + * Parse /etc/environment lines ala AIX and Linux. * Lines may be in either of three formats: * NAME=VALUE * NAME="VALUE" @@ -1198,24 +1248,24 @@ validate_env_vars(char * const env_vars[]) * Invalid lines, blank lines, or lines consisting solely of a comment * character are skipped. */ -bool -read_env_file(const char *path, bool overwrite, bool restricted) +static char * +env_file_next_local(void *cookie, int *errnum) { - FILE *fp; - bool ret = true; - char *cp, *var, *val, *line = NULL; - size_t var_len, val_len, linesize = 0; - debug_decl(read_env_file, SUDOERS_DEBUG_ENV) - - if ((fp = fopen(path, "r")) == NULL) { - if (errno != ENOENT) - ret = false; - debug_return_bool(ret); - } + struct env_file_local *efl = cookie; + char *var, *val, *ret = NULL; + size_t var_len, val_len; + debug_decl(env_file_next_local, SUDOERS_DEBUG_ENV) + + *errnum = 0; + for (;;) { + if (sudo_parseln(&efl->line, &efl->linesize, NULL, efl->fp, PARSELN_CONT_IGN) == -1) { + if (!feof(efl->fp)) + *errnum = errno; + break; + } - while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_CONT_IGN) != -1) { /* Skip blank or comment lines */ - if (*(var = line) == '\0') + if (*(var = efl->line) == '\0') continue; /* Skip optional "export " */ @@ -1234,15 +1284,6 @@ read_env_file(const char *path, bool overwrite, bool restricted) var_len = (size_t)(val - var); val_len = strlen(++val); - /* - * If the env file is restricted, apply env_check and env_keep - * when env_reset is set or env_delete when it is not. - */ - if (restricted) { - if (def_env_reset ? !env_should_keep(var) : env_should_delete(var)) - continue; - } - /* Strip leading and trailing single/double quotes */ if ((val[0] == '\'' || val[0] == '\"') && val[0] == val[val_len - 1]) { val[val_len - 1] = '\0'; @@ -1250,25 +1291,91 @@ read_env_file(const char *path, bool overwrite, bool restricted) val_len -= 2; } - if ((cp = malloc(var_len + 1 + val_len + 1)) == NULL) { + if ((ret = malloc(var_len + 1 + val_len + 1)) == NULL) { + *errnum = errno; sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "unable to allocate memory"); - /* XXX - no undo on failure */ - ret = false; + } else { + memcpy(ret, var, var_len + 1); /* includes '=' */ + memcpy(ret + var_len + 1, val, val_len + 1); /* includes NUL */ + sudoers_gc_add(GC_PTR, ret); + } + break; + } + debug_return_str(ret); +} + +static struct sudoers_env_file env_file_sudoers = { + env_file_open_local, + env_file_close_local, + env_file_next_local +}; + +static struct sudoers_env_file env_file_system = { + env_file_open_local, + env_file_close_local, + env_file_next_local +}; + +void +register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *), + char * (*ef_next)(void *, int *), bool system) +{ + struct sudoers_env_file *ef = system ? &env_file_system : &env_file_sudoers; + + ef->open = ef_open; + ef->close = ef_close; + ef->next = ef_next; +} + +bool +read_env_file(const char *path, bool overwrite, bool restricted) +{ + struct sudoers_env_file *ef; + bool ret = true; + char *envstr; + void *cookie; + int errnum; + debug_decl(read_env_file, SUDOERS_DEBUG_ENV) + + /* + * The environment file may be handled differently depending on + * whether it is specified in sudoers or the system. + */ + if (path == def_env_file || path == def_restricted_env_file) + ef = &env_file_sudoers; + else + ef = &env_file_system; + + cookie = ef->open(path); + if (cookie == NULL) + debug_return_bool(false); + + for (;;) { + /* Keep reading until EOF or error. */ + if ((envstr = ef->next(cookie, &errnum)) == NULL) { + if (errnum != 0) + ret = false; break; } - memcpy(cp, var, var_len + 1); /* includes '=' */ - memcpy(cp + var_len + 1, val, val_len + 1); /* includes NUL */ - sudoers_gc_add(GC_PTR, cp); - if (sudo_putenv(cp, true, overwrite) == -1) { + /* + * If the env file is restricted, apply env_check and env_keep + * when env_reset is set or env_delete when it is not. + */ + if (restricted) { + if (def_env_reset ? !env_should_keep(envstr) : env_should_delete(envstr)) { + free(envstr); + continue; + } + } + if (sudo_putenv(envstr, true, overwrite) == -1) { /* XXX - no undo on failure */ ret = false; break; } } - free(line); - fclose(fp); + ef->close(cookie); debug_return_bool(ret); } diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index b12873967..25d6b2d97 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -377,6 +377,7 @@ int sudoers_hook_getenv(const char *name, char **value, void *closure); int sudoers_hook_putenv(char *string, void *closure); int sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure); int sudoers_hook_unsetenv(const char *name, void *closure); +void register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *), char * (*ef_next)(void *, int *), bool system); /* env_pattern.c */ bool matches_env_pattern(const char *pattern, const char *var, bool *full_match);