diff options
Diffstat (limited to 'libpam/pam_env.c')
-rw-r--r-- | libpam/pam_env.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/libpam/pam_env.c b/libpam/pam_env.c new file mode 100644 index 0000000..1c8403d --- /dev/null +++ b/libpam/pam_env.c @@ -0,0 +1,392 @@ +/* + * pam_env.c + * + * Copyright (c) Andrew G. Morgan <morgan@parc.power.net> 1996,1997 + * All rights reserved. + * + * This file was written from a "hint" provided by the people at SUN. + * and the X/Open XSSO draft of March 1997. + * + * $Id$ + */ + +#include "pam_private.h" + +#include <string.h> +#include <stdlib.h> + +#ifdef sunos +#define memmove(x,y,z) bcopy(y,x,z) +#endif + +/* helper functions */ + +#ifdef PAM_DEBUG +static void _pam_dump_env(pam_handle_t *pamh) +{ + int i; + + D(("Listing environment of pamh=%p", pamh)); + D(("pamh->env = %p", pamh->env)); + D(("environment entries used = %d [of %d allocated]" + , pamh->env->requested, pamh->env->entries)); + + for (i=0; i<pamh->env->requested; ++i) { + _pam_output_debug(">%-3d [%9p]:[%s]" + , i, pamh->env->list[i], pamh->env->list[i]); + } + _pam_output_debug("*NOTE* the last item should be (nil)"); +} +#else +#define _pam_dump_env(x) +#endif + +/* + * Create the environment + */ + +int _pam_make_env(pam_handle_t *pamh) +{ + D(("called.")); + + IF_NO_PAMH("_pam_make_env", pamh, PAM_ABORT); + + /* + * get structure memory + */ + + pamh->env = (struct pam_environ *) malloc(sizeof(struct pam_environ)); + if (pamh->env == NULL) { + pam_syslog(pamh, LOG_CRIT, "_pam_make_env: out of memory"); + return PAM_BUF_ERR; + } + + /* + * get list memory + */ + + pamh->env->list = (char **)calloc( PAM_ENV_CHUNK, sizeof(char *) ); + if (pamh->env->list == NULL) { + pam_syslog(pamh, LOG_CRIT, "_pam_make_env: no memory for list"); + _pam_drop(pamh->env); + return PAM_BUF_ERR; + } + + /* + * fill entries in pamh->env + */ + + pamh->env->entries = PAM_ENV_CHUNK; + pamh->env->requested = 1; + pamh->env->list[0] = NULL; + + _pam_dump_env(pamh); /* only active when debugging */ + + return PAM_SUCCESS; +} + +/* + * purge the environment + */ + +void _pam_drop_env(pam_handle_t *pamh) +{ + D(("called.")); + IF_NO_PAMH("_pam_make_env", pamh, /* nothing to return */); + + if (pamh->env != NULL) { + int i; + /* we will only purge the pamh->env->requested number of elements */ + + for (i=pamh->env->requested-1; i-- > 0; ) { + D(("dropping #%3d>%s<", i, pamh->env->list[i])); + _pam_overwrite(pamh->env->list[i]); /* clean */ + _pam_drop(pamh->env->list[i]); /* forget */ + } + pamh->env->requested = 0; + pamh->env->entries = 0; + _pam_drop(pamh->env->list); /* forget */ + _pam_drop(pamh->env); /* forget */ + } else { + D(("no environment present in pamh?")); + } +} + +/* + * Return the item number of the given variable = first 'length' chars + * of 'name_value'. Since this is a static function, it is safe to + * assume its supplied arguments are well defined. + */ + +static int _pam_search_env(const struct pam_environ *env + , const char *name_value, int length) +{ + int i; + + for (i=env->requested-1; i-- > 0; ) { + if (strncmp(name_value,env->list[i],length) == 0 + && env->list[i][length] == '=') { + + return i; /* Got it! */ + + } + } + + return -1; /* no luck */ +} + +/* + * externally visible functions + */ + +/* + * pam_putenv(): Add/replace/delete a PAM-environment variable. + * + * Add/replace: + * name_value = "NAME=VALUE" or "NAME=" (for empty value="\0") + * + * delete: + * name_value = "NAME" + */ + +int pam_putenv(pam_handle_t *pamh, const char *name_value) +{ + int l2eq, item, retval; + + D(("called.")); + IF_NO_PAMH("pam_putenv", pamh, PAM_ABORT); + + if (name_value == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_putenv: no variable indicated"); + return PAM_PERM_DENIED; + } + + /* + * establish if we are setting or deleting; scan for '=' + */ + + for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq); + if (l2eq <= 0) { + pam_syslog(pamh, LOG_ERR, "pam_putenv: bad variable"); + return PAM_BAD_ITEM; + } + + /* + * Look first for environment. + */ + + if (pamh->env == NULL || pamh->env->list == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_putenv: no env%s found", + pamh->env == NULL ? "":"-list"); + return PAM_ABORT; + } + + /* find the item to replace */ + + item = _pam_search_env(pamh->env, name_value, l2eq); + + if (name_value[l2eq]) { /* (re)setting */ + + if (item == -1) { /* new variable */ + D(("adding item: %s", name_value)); + /* enough space? */ + if (pamh->env->entries <= pamh->env->requested) { + register int i; + register char **tmp; + + /* get some new space */ + tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK + , sizeof(char *) ); + if (tmp == NULL) { + /* nothing has changed - old env intact */ + pam_syslog(pamh, LOG_CRIT, + "pam_putenv: cannot grow environment"); + return PAM_BUF_ERR; + } + + /* copy old env-item pointers/forget old */ + for (i=0; i<pamh->env->requested; ++i) { + tmp[i] = pamh->env->list[i]; + pamh->env->list[i] = NULL; + } + + /* drop old list and replace with new */ + _pam_drop(pamh->env->list); + pamh->env->list = tmp; + pamh->env->entries += PAM_ENV_CHUNK; + + D(("resized env list")); + _pam_dump_env(pamh); /* only when debugging */ + } + + item = pamh->env->requested-1; /* old last item (NULL) */ + + /* add a new NULL entry at end; increase counter */ + pamh->env->list[pamh->env->requested++] = NULL; + + } else { /* replace old */ + D(("replacing item: %s\n with: %s" + , pamh->env->list[item], name_value)); + _pam_overwrite(pamh->env->list[item]); + _pam_drop(pamh->env->list[item]); + } + + /* + * now we have a place to put the new env-item, insert at 'item' + */ + + pamh->env->list[item] = _pam_strdup(name_value); + if (pamh->env->list[item] != NULL) { + _pam_dump_env(pamh); /* only when debugging */ + return PAM_SUCCESS; + } + + /* something went wrong; we should delete the item - fall through */ + + retval = PAM_BUF_ERR; /* an error occurred */ + } else { + retval = PAM_SUCCESS; /* we requested delete */ + } + + /* getting to here implies we are deleting an item */ + + if (item < 0) { + pam_syslog(pamh, LOG_ERR, + "pam_putenv: delete non-existent entry; %s", name_value); + return PAM_BAD_ITEM; + } + + /* + * remove item: purge memory; reset counter; resize [; display-env] + */ + + D(("deleting: env#%3d:[%s]", item, pamh->env->list[item])); + _pam_overwrite(pamh->env->list[item]); + _pam_drop(pamh->env->list[item]); + --(pamh->env->requested); + D(("mmove: item[%d]+%d -> item[%d]" + , item+1, ( pamh->env->requested - item ), item)); + (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1] + , ( pamh->env->requested - item )*sizeof(char *) ); + + _pam_dump_env(pamh); /* only when debugging */ + + /* + * deleted. + */ + + return retval; +} + +/* + * Return the value of the requested environment variable + */ + +const char *pam_getenv(pam_handle_t *pamh, const char *name) +{ + int item; + + D(("called.")); + IF_NO_PAMH("pam_getenv", pamh, NULL); + + if (name == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenv: no variable indicated"); + return NULL; + } + + if (pamh->env == NULL || pamh->env->list == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenv: no env%s found", + pamh->env == NULL ? "":"-list" ); + return NULL; + } + + /* find the requested item */ + + item = _pam_search_env(pamh->env, name, strlen(name)); + if (item != -1) { + + D(("env-item: %s, found!", name)); + return (pamh->env->list[item] + 1 + strlen(name)); + + } else { + + D(("env-item: %s, not found", name)); + return NULL; + + } +} + +static char **_copy_env(pam_handle_t *pamh) +{ + char **dump; + int i = pamh->env->requested; /* reckon size of environment */ + char *const *env = pamh->env->list; + + D(("now get some memory for dump")); + + /* allocate some memory for this (plus the null tail-pointer) */ + dump = (char **) calloc(i, sizeof(char *)); + D(("dump = %p", dump)); + if (dump == NULL) { + return NULL; + } + + /* now run through entries and copy the variables over */ + dump[--i] = NULL; + while (i-- > 0) { + D(("env[%d]=`%s'", i,env[i])); + dump[i] = _pam_strdup(env[i]); + D(("->dump[%d]=`%s'", i,dump[i])); + if (dump[i] == NULL) { + /* out of memory */ + + while (dump[++i]) { + _pam_overwrite(dump[i]); + _pam_drop(dump[i]); + } + _pam_drop(dump); + return NULL; + } + } + + env = NULL; /* forget now */ + + /* return transcribed environment */ + return dump; +} + +char **pam_getenvlist(pam_handle_t *pamh) +{ + int i; + + D(("called.")); + IF_NO_PAMH("pam_getenvlist", pamh, NULL); + + if (pamh->env == NULL || pamh->env->list == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenvlist: no env%s found", + pamh->env == NULL ? "":"-list" ); + return NULL; + } + + /* some quick checks */ + + if (pamh->env->requested > pamh->env->entries) { + pam_syslog(pamh, LOG_ERR, "pam_getenvlist: environment corruption"); + _pam_dump_env(pamh); /* only active when debugging */ + return NULL; + } + + for (i=pamh->env->requested-1; i-- > 0; ) { + if (pamh->env->list[i] == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenvlist: environment broken"); + _pam_dump_env(pamh); /* only active when debugging */ + return NULL; /* somehow we've broken the environment!? */ + } + } + + /* Seems fine; copy environment */ + + _pam_dump_env(pamh); /* only active when debugging */ + + return _copy_env(pamh); +} |