File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / env_hooks.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:26:49 2012 UTC (12 years, 1 month ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /*
    2:  * Copyright (c) 2010, 2012 Todd C. Miller <Todd.Miller@courtesan.com>
    3:  *
    4:  * Permission to use, copy, modify, and distribute this software for any
    5:  * purpose with or without fee is hereby granted, provided that the above
    6:  * copyright notice and this permission notice appear in all copies.
    7:  *
    8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   15:  */
   16: 
   17: #include <config.h>
   18: 
   19: #include <sys/types.h>
   20: 
   21: #include <stdio.h>
   22: #ifdef STDC_HEADERS
   23: # include <stdlib.h>
   24: # include <stddef.h>
   25: #else
   26: # ifdef HAVE_STDLIB_H
   27: #  include <stdlib.h>
   28: # endif
   29: #endif /* STDC_HEADERS */
   30: #ifdef HAVE_STRING_H
   31: # include <string.h>
   32: #endif /* HAVE_STRING_H */
   33: #ifdef HAVE_STRINGS_H
   34: # include <strings.h>
   35: #endif /* HAVE_STRINGS_H */
   36: #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
   37: # include <malloc.h>
   38: #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
   39: #include <errno.h>
   40: #ifdef HAVE_DLOPEN
   41: # include <dlfcn.h>
   42: #else
   43: # include "compat/dlfcn.h"
   44: #endif
   45: 
   46: #include "sudo.h"
   47: #include "sudo_plugin.h"
   48: 
   49: extern char **environ;		/* global environment pointer */
   50: static char **priv_environ;	/* private environment pointer */
   51: 
   52: static char *
   53: rpl_getenv(const char *name)
   54: {
   55:     char **ep, *val = NULL;
   56:     size_t namelen = 0;
   57: 
   58:     /* For BSD compatibility, treat '=' in name like end of string. */
   59:     while (name[namelen] != '\0' && name[namelen] != '=')
   60: 	namelen++;
   61:     for (ep = environ; *ep != NULL; ep++) {
   62: 	if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
   63: 	    val = *ep + namelen + 1;
   64: 	    break;
   65: 	}
   66:     }
   67:     return val;
   68: }
   69: 
   70: typedef char * (*sudo_fn_getenv_t)(const char *);
   71: 
   72: char *
   73: getenv(const char *name)
   74: {
   75:     char *val = NULL;
   76: 
   77:     switch (process_hooks_getenv(name, &val)) {
   78: 	case SUDO_HOOK_RET_STOP:
   79: 	    return val;
   80: 	case SUDO_HOOK_RET_ERROR:
   81: 	    return NULL;
   82: 	default: {
   83: #if defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
   84: 	    sudo_fn_getenv_t fn;
   85: 
   86: 	    fn = (sudo_fn_getenv_t)dlsym(RTLD_NEXT, "getenv");
   87: 	    if (fn != NULL)
   88: 		return fn(name);
   89: #endif /* HAVE_DLOPEN && RTLD_NEXT */
   90: 	    return rpl_getenv(name);
   91: 	}
   92:     }
   93: }
   94: 
   95: static int
   96: rpl_putenv(PUTENV_CONST char *string)
   97: {
   98:     char **ep;
   99:     size_t len;
  100:     bool found = false;
  101: 
  102:     /* Look for existing entry. */
  103:     len = (strchr(string, '=') - string) + 1;
  104:     for (ep = environ; *ep != NULL; ep++) {
  105: 	if (strncmp(string, *ep, len) == 0) {
  106: 	    *ep = (char *)string;
  107: 	    found = true;
  108: 	    break;
  109: 	}
  110:     }
  111:     /* Prune out duplicate variables. */
  112:     if (found) {
  113: 	while (*ep != NULL) {
  114: 	    if (strncmp(string, *ep, len) == 0) {
  115: 		char **cur = ep;
  116: 		while ((*cur = *(cur + 1)) != NULL)
  117: 		    cur++;
  118: 	    } else {
  119: 		ep++;
  120: 	    }
  121: 	}
  122:     }
  123: 
  124:     /* Append at the end if not already found. */
  125:     if (!found) {
  126: 	size_t env_len = (size_t)(ep - environ);
  127: 	char **envp = erealloc3(priv_environ, env_len + 2, sizeof(char *));
  128: 	if (environ != priv_environ)
  129: 	    memcpy(envp, environ, env_len * sizeof(char *));
  130: 	envp[env_len++] = (char *)string;
  131: 	envp[env_len] = NULL;
  132: 	priv_environ = environ = envp;
  133:     }
  134:     return 0;
  135: }
  136: 
  137: typedef int (*sudo_fn_putenv_t)(PUTENV_CONST char *);
  138: 
  139: int
  140: putenv(PUTENV_CONST char *string)
  141: {
  142:     switch (process_hooks_putenv((char *)string)) {
  143: 	case SUDO_HOOK_RET_STOP:
  144: 	    return 0;
  145: 	case SUDO_HOOK_RET_ERROR:
  146: 	    return -1;
  147: 	default: {
  148: #if defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
  149: 	    sudo_fn_putenv_t fn;
  150: 
  151: 	    fn = (sudo_fn_putenv_t)dlsym(RTLD_NEXT, "putenv");
  152: 	    if (fn != NULL)
  153: 		return fn(string);
  154: #endif /* HAVE_DLOPEN && RTLD_NEXT */
  155: 	    return rpl_putenv(string);
  156: 	}
  157:     }
  158: }
  159: 
  160: static int
  161: rpl_setenv(const char *var, const char *val, int overwrite)
  162: {
  163:     char *envstr, *dst;
  164:     const char *src;
  165:     size_t esize;
  166: 
  167:     if (!var || *var == '\0') {
  168: 	errno = EINVAL;
  169: 	return -1;
  170:     }
  171: 
  172:     /*
  173:      * POSIX says a var name with '=' is an error but BSD
  174:      * just ignores the '=' and anything after it.
  175:      */
  176:     for (src = var; *src != '\0' && *src != '='; src++)
  177: 	;
  178:     esize = (size_t)(src - var) + 2;
  179:     if (val) {
  180:         esize += strlen(val);	/* glibc treats a NULL val as "" */
  181:     }
  182: 
  183:     /* Allocate and fill in envstr. */
  184:     if ((envstr = malloc(esize)) == NULL)
  185: 	return -1;
  186:     for (src = var, dst = envstr; *src != '\0' && *src != '=';)
  187: 	*dst++ = *src++;
  188:     *dst++ = '=';
  189:     if (val) {
  190: 	for (src = val; *src != '\0';)
  191: 	    *dst++ = *src++;
  192:     }
  193:     *dst = '\0';
  194: 
  195:     if (!overwrite && getenv(var) != NULL) {
  196: 	free(envstr);
  197: 	return 0;
  198:     }
  199:     return rpl_putenv(envstr);
  200: }
  201: 
  202: typedef int (*sudo_fn_setenv_t)(const char *, const char *, int);
  203: 
  204: int
  205: setenv(const char *var, const char *val, int overwrite)
  206: {
  207:     switch (process_hooks_setenv(var, val, overwrite)) {
  208: 	case SUDO_HOOK_RET_STOP:
  209: 	    return 0;
  210: 	case SUDO_HOOK_RET_ERROR:
  211: 	    return -1;
  212: 	default: {
  213: #if defined(HAVE_SETENV) && defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
  214: 	    sudo_fn_setenv_t fn;
  215: 
  216: 	    fn = (sudo_fn_setenv_t)dlsym(RTLD_NEXT, "setenv");
  217: 	    if (fn != NULL)
  218: 		return fn(var, val, overwrite);
  219: #endif /* HAVE_SETENV && HAVE_DLOPEN && RTLD_NEXT */
  220: 	    return rpl_setenv(var, val, overwrite);
  221: 	}
  222:     }
  223: }
  224: 
  225: #ifdef UNSETENV_VOID
  226: static void
  227: #else
  228: int
  229: #endif
  230: rpl_unsetenv(const char *var)
  231: {
  232:     char **ep = environ;
  233:     size_t len;
  234: 
  235:     if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
  236: 	errno = EINVAL;
  237: #ifdef UNSETENV_VOID
  238: 	return;
  239: #else
  240: 	return -1;
  241: #endif
  242:     }
  243: 
  244:     len = strlen(var);
  245:     while (*ep != NULL) {
  246: 	if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
  247: 	    /* Found it; shift remainder + NULL over by one. */
  248: 	    char **cur = ep;
  249: 	    while ((*cur = *(cur + 1)) != NULL)
  250: 		cur++;
  251: 	    /* Keep going, could be multiple instances of the var. */
  252: 	} else {
  253: 	    ep++;
  254: 	}
  255:     }
  256: #ifndef UNSETENV_VOID
  257:     return 0;
  258: #endif
  259: }
  260: 
  261: #ifdef UNSETENV_VOID
  262: typedef void (*sudo_fn_unsetenv_t)(const char *);
  263: #else
  264: typedef int (*sudo_fn_unsetenv_t)(const char *);
  265: #endif
  266: 
  267: #ifdef UNSETENV_VOID
  268: void
  269: unsetenv(const char *var)
  270: {
  271:     switch (process_hooks_unsetenv(var)) {
  272: 	case SUDO_HOOK_RET_STOP:
  273: 	    return 0;
  274: 	case SUDO_HOOK_RET_ERROR:
  275: 	    return -1;
  276: 	default: {
  277: #if defined(HAVE_UNSETENV) && defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
  278: 	    sudo_fn_unsetenv_t fn;
  279: 
  280: 	    fn = (sudo_fn_unsetenv_t)dlsym(RTLD_NEXT, "unsetenv");
  281: 	    if (fn != NULL)
  282: 		fn(var);
  283: 	    else
  284: #endif /* HAVE_UNSETENV && HAVE_DLOPEN && RTLD_NEXT */
  285: 		rpl_unsetenv(var);
  286: 	}
  287:     }
  288: }
  289: #else
  290: int
  291: unsetenv(const char *var)
  292: {
  293:     switch (process_hooks_unsetenv(var)) {
  294: 	case SUDO_HOOK_RET_STOP:
  295: 	    return 0;
  296: 	case SUDO_HOOK_RET_ERROR:
  297: 	    return -1;
  298: 	default: {
  299: #if defined(HAVE_UNSETENV) && defined(HAVE_DLOPEN) && defined(RTLD_NEXT)
  300: 	    sudo_fn_unsetenv_t fn;
  301: 
  302: 	    fn = (sudo_fn_unsetenv_t)dlsym(RTLD_NEXT, "unsetenv");
  303: 	    if (fn != NULL)
  304: 		return fn(var);
  305: #endif /* HAVE_UNSETENV && HAVE_DLOPEN && RTLD_NEXT */
  306: 	    return rpl_unsetenv(var);
  307: 	}
  308:     }
  309: }
  310: #endif /* UNSETENV_VOID */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>