]> granicus.if.org Git - sudo/commitdiff
Add simple API for to allow reading environment data from different sources.
authorTodd C. Miller <Todd.Miller@sudo.ws>
Thu, 21 Feb 2019 00:05:02 +0000 (17:05 -0700)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Thu, 21 Feb 2019 00:05:02 +0000 (17:05 -0700)
Currently, this is used to read a file like /etc/environment.

plugins/sudoers/env.c
plugins/sudoers/sudoers.h

index f908a3e592187dfb162adca63f75800e92870b0c..1efdc6a9a12ca2299970e93f88552c789792b7a6 100644 (file)
 # 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);
 }
index b1287396748204bd0a58403b6d76bf1e0fb3690b..25d6b2d976016788f66ba8049df21cf020d5e3eb 100644 (file)
@@ -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);