int pam_get_data( const pam_handle_t *pamh, const char *module_data_name, const void **datap) { struct pam_data *data; D(("called")); IF_NO_PAMH("pam_get_data", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_APP(pamh)) { D(("called from application!?")); return PAM_SYSTEM_ERR; } /* module_data_name should not be NULL */ if (module_data_name == NULL) { D(("called with NULL as module_data_name")); return PAM_SYSTEM_ERR; } data = _pam_locate_data(pamh, module_data_name); if (data) { *datap = data->data; return PAM_SUCCESS; } return PAM_NO_MODULE_DATA; }
int pam_authenticate(pam_handle_t *pamh, int flags) { int retval; D(("pam_authenticate called")); IF_NO_PAMH("pam_authenticate", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } if (pamh->former.choice == PAM_NOT_STACKED) { _pam_sanitize(pamh); _pam_start_timer(pamh); /* we try to make the time for a failure independent of the time it takes to fail */ } retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE); if (retval != PAM_INCOMPLETE) { _pam_sanitize(pamh); _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ D(("pam_authenticate exit")); } else { D(("will resume when ready")); } return retval; }
int pam_setcred(pam_handle_t *pamh, int flags) { int retval; D(("pam_setcred called")); IF_NO_PAMH("pam_setcred", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } if (! flags) { flags = PAM_ESTABLISH_CRED; } retval = _pam_dispatch(pamh, flags, PAM_SETCRED); #if HAVE_LIBAUDIT retval = _pam_auditlog(pamh, PAM_SETCRED, retval, flags); #endif D(("pam_setcred exit")); return retval; }
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; } }
int pam_set_data( pam_handle_t *pamh, const char *module_data_name, void *data, void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) { struct pam_data *data_entry; D(("called")); IF_NO_PAMH("pam_set_data", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_APP(pamh)) { D(("called from application!?")); return PAM_SYSTEM_ERR; } /* module_data_name should not be NULL */ if (module_data_name == NULL) { D(("called with NULL as module_data_name")); return PAM_SYSTEM_ERR; } /* first check if there is some data already. If so clean it up */ if ((data_entry = _pam_locate_data(pamh, module_data_name))) { if (data_entry->cleanup) { data_entry->cleanup(pamh, data_entry->data, PAM_DATA_REPLACE | PAM_SUCCESS ); } } else if ((data_entry = malloc(sizeof(*data_entry)))) { char *tname; if ((tname = _pam_strdup(module_data_name)) == NULL) { pam_syslog(pamh, LOG_CRIT, "pam_set_data: no memory for data name"); _pam_drop(data_entry); return PAM_BUF_ERR; } data_entry->next = pamh->data; pamh->data = data_entry; data_entry->name = tname; } else { pam_syslog(pamh, LOG_CRIT, "pam_set_data: cannot allocate data entry"); return PAM_BUF_ERR; } data_entry->data = data; /* note this could be NULL */ data_entry->cleanup = cleanup; return PAM_SUCCESS; }
int pam_close_session(pam_handle_t *pamh, int flags) { D(("called")); IF_NO_PAMH("pam_close_session", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } return _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION); }
int pam_chauthtok(pam_handle_t *pamh, int flags) { int retval; D(("called.")); IF_NO_PAMH("pam_chauthtok", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } /* applications are not allowed to set this flags */ if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK)) { pam_syslog (pamh, LOG_ERR, "PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK set by application"); return PAM_SYSTEM_ERR; } if (pamh->former.choice == PAM_NOT_STACKED) { _pam_start_timer(pamh); /* we try to make the time for a failure independent of the time it takes to fail */ _pam_sanitize(pamh); pamh->former.update = PAM_FALSE; } /* first call to check if there will be a problem */ if (pamh->former.update || (retval = _pam_dispatch(pamh, flags|PAM_PRELIM_CHECK, PAM_CHAUTHTOK)) == PAM_SUCCESS) { D(("completed check ok: former=%d", pamh->former.update)); pamh->former.update = PAM_TRUE; retval = _pam_dispatch(pamh, flags|PAM_UPDATE_AUTHTOK, PAM_CHAUTHTOK); } /* if we completed we should clean up */ if (retval != PAM_INCOMPLETE) { _pam_sanitize(pamh); pamh->former.update = PAM_FALSE; _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ D(("pam_chauthtok exit %d - %d", retval, pamh->former.choice)); } else { D(("will resume when ready", retval)); } return retval; }
int pam_close_session(pam_handle_t *pamh, int flags) { int retval; D(("called")); IF_NO_PAMH("pam_close_session", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } retval = _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION); #if HAVE_LIBAUDIT retval = _pam_auditlog(pamh, PAM_CLOSE_SESSION, retval, flags); #endif #ifdef PAM_STATS if (retval != PAM_SUCCESS) { char usr[MAX_PAM_STATS_USR_SIZE]; char buf[MAX_PAM_STATS_BUF_SIZE]; usr[MAX_PAM_STATS_USR_SIZE-1]='\0'; strncpy(usr,(retval == PAM_USER_UNKNOWN)?"unknown":pamh->user, MAX_PAM_STATS_USR_SIZE-1); memset(buf,'\0',MAX_PAM_STATS_BUF_SIZE); snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd -a incr pam_failed_%s %s \\;" " push pam_last_failure_%s %s \"%s\" 0 \\;" " incr pam_users %s\\;" " incr pam_services %s", usr, pamh->service_name, usr, pamh->service_name, pam_strerror(pamh, retval), usr, pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } } #endif return retval; }
int pam_open_session(pam_handle_t *pamh, int flags) { int retval; D(("called")); IF_NO_PAMH("pam_open_session", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } retval = _pam_dispatch(pamh, flags, PAM_OPEN_SESSION); return retval; }
static struct pam_data *_pam_locate_data(const pam_handle_t *pamh, const char *name) { struct pam_data *data; D(("called")); IF_NO_PAMH("_pam_locate_data", pamh, NULL); data = pamh->data; while (data) { if (!strcmp(data->name, name)) { return data; } data = data->next; } return NULL; }
int pam_acct_mgmt(pam_handle_t *pamh, int flags) { int retval; D(("called")); IF_NO_PAMH("pam_acct_mgmt", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } retval = _pam_dispatch(pamh, flags, PAM_ACCOUNT); #ifdef HAVE_LIBAUDIT retval = _pam_auditlog(pamh, PAM_ACCOUNT, retval, flags); #endif return retval; }
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; }
int pam_fail_delay(pam_handle_t *pamh, unsigned int usec) { int largest; IF_NO_PAMH("pam_fail_delay", pamh, PAM_SYSTEM_ERR); D(("setting delay to %u",usec)); if (pamh->fail_delay.set) { largest = pamh->fail_delay.delay; } else { pamh->fail_delay.set = PAM_TRUE; largest = 0; } D(("largest = %u",largest)); if (largest < usec) { D(("resetting largest delay")); pamh->fail_delay.delay = usec; } return PAM_SUCCESS; }
int pam_authenticate_secondary(pam_handle_t *pamh, char *target_username, char *target_module_type, char *target_authn_domain, char *target_supp_data, unsigned char *target_module_authtok, int flags) { int retval=PAM_SYSTEM_ERR; D(("called")); _pam_start_timer(pamh); /* we try to make the time for a failure independent of the time it takes to fail */ IF_NO_PAMH("pam_authenticate_secondary",pamh,PAM_SYSTEM_ERR); _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ D(("pam_authenticate_secondary exit")); return retval; }
int pam_set_item (pam_handle_t *pamh, int item_type, const void *item) { int retval; D(("called")); IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR); retval = PAM_SUCCESS; switch (item_type) { case PAM_SERVICE: /* Setting handlers_loaded to 0 will cause the handlers * to be reloaded on the next call to a service module. */ pamh->handlers.handlers_loaded = 0; RESET(pamh->service_name, item); { char *tmp; for (tmp=pamh->service_name; *tmp; ++tmp) *tmp = tolower(*tmp); /* require lower case */ } break; case PAM_USER: RESET(pamh->user, item); break; case PAM_USER_PROMPT: RESET(pamh->prompt, item); break; case PAM_TTY: D(("setting tty to %s", item)); RESET(pamh->tty, item); break; case PAM_RUSER: RESET(pamh->ruser, item); break; case PAM_RHOST: RESET(pamh->rhost, item); break; case PAM_AUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { char *_TMP_ = pamh->authtok; if (_TMP_ == item) /* not changed so leave alone */ break; pamh->authtok = (item) ? _pam_strdup(item) : NULL; if (_TMP_) { _pam_overwrite(_TMP_); free(_TMP_); } } else { retval = PAM_BAD_ITEM; } break; case PAM_OLDAUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { char *_TMP_ = pamh->oldauthtok; if (_TMP_ == item) /* not changed so leave alone */ break; pamh->oldauthtok = (item) ? _pam_strdup(item) : NULL; if (_TMP_) { _pam_overwrite(_TMP_); free(_TMP_); } } else { retval = PAM_BAD_ITEM; } break; case PAM_CONV: /* want to change the conversation function */ if (item == NULL) { _pam_system_log(LOG_ERR, "pam_set_item: attempt to set conv() to NULL"); retval = PAM_PERM_DENIED; } else { struct pam_conv *tconv; if ((tconv= (struct pam_conv *) malloc(sizeof(struct pam_conv)) ) == NULL) { _pam_system_log(LOG_CRIT, "pam_set_item: malloc failed for pam_conv"); retval = PAM_BUF_ERR; } else { memcpy(tconv, item, sizeof(struct pam_conv)); _pam_drop(pamh->pam_conversation); pamh->pam_conversation = tconv; } } break; case PAM_FAIL_DELAY: pamh->fail_delay.delay_fn_ptr = item; break; default: retval = PAM_BAD_ITEM; } return retval; }
int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt) { const char *use_prompt; int retval; struct pam_message msg,*pmsg; struct pam_response *resp; D(("called.")); IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR); if (pamh->pam_conversation == NULL) { _pam_system_log(LOG_ERR, "pam_get_user: no conv element in pamh"); return PAM_SERVICE_ERR; } if (user == NULL) { /* ensure the the module has suplied a destination */ _pam_system_log(LOG_ERR, "pam_get_user: nowhere to record username"); return PAM_PERM_DENIED; } else *user = NULL; if (pamh->user) { /* have one so return it */ *user = pamh->user; return PAM_SUCCESS; } /* will need a prompt */ use_prompt = prompt; if (use_prompt == NULL) { use_prompt = pamh->prompt; if (use_prompt == NULL) { use_prompt = PAM_DEFAULT_PROMPT; } } /* If we are resuming an old conversation, we verify that the prompt is the same. Anything else is an error. */ if (pamh->former.want_user) { /* must have a prompt to resume with */ if (! pamh->former.prompt) { _pam_system_log(LOG_ERR, "pam_get_user: failed to resume with prompt" ); return PAM_ABORT; } /* must be the same prompt as last time */ if (strcmp(pamh->former.prompt, use_prompt)) { _pam_system_log(LOG_ERR, "pam_get_user: resumed with different prompt"); return PAM_ABORT; } /* ok, we can resume where we left off last time */ pamh->former.want_user = PAM_FALSE; _pam_overwrite(pamh->former.prompt); _pam_drop(pamh->former.prompt); } /* converse with application -- prompt user for a username */ pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_ON; msg.msg = use_prompt; resp = NULL; retval = pamh->pam_conversation-> conv(1, (const struct pam_message **) &pmsg, &resp, pamh->pam_conversation->appdata_ptr); if (retval == PAM_CONV_AGAIN) { /* conversation function is waiting for an event - save state */ D(("conversation function is not ready yet")); pamh->former.want_user = PAM_TRUE; pamh->former.prompt = _pam_strdup(use_prompt); } else if (resp == NULL) { /* * conversation should have given a response */ D(("pam_get_user: no response provided")); retval = PAM_CONV_ERR; } else if (retval == PAM_SUCCESS) { /* copy the username */ /* * now we set the PAM_USER item -- this was missing from pre.53 * releases. However, reading the Sun manual, it is part of * the standard API. */ RESET(pamh->user, resp->resp); *user = pamh->user; } if (resp) { /* * note 'resp' is allocated by the application and is * correctly free()'d here */ _pam_drop_reply(resp, 1); } D(("completed")); return retval; /* pass on any error from conversation */ }
int pam_get_item (const pam_handle_t *pamh, int item_type, const void **item) { int retval = PAM_SUCCESS; D(("called.")); IF_NO_PAMH("pam_get_item", pamh, PAM_SYSTEM_ERR); if (item == NULL) { _pam_system_log(LOG_ERR, "pam_get_item: nowhere to place requested item"); return PAM_PERM_DENIED; } switch (item_type) { case PAM_SERVICE: *item = pamh->service_name; break; case PAM_USER: D(("returning user=%s", pamh->user)); *item = pamh->user; break; case PAM_USER_PROMPT: D(("returning userprompt=%s", pamh->user)); *item = pamh->prompt; break; case PAM_TTY: D(("returning tty=%s", pamh->tty)); *item = pamh->tty; break; case PAM_RUSER: *item = pamh->ruser; break; case PAM_RHOST: *item = pamh->rhost; break; case PAM_AUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { *item = pamh->authtok; } else { retval = PAM_BAD_ITEM; } break; case PAM_OLDAUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { *item = pamh->oldauthtok; } else { retval = PAM_BAD_ITEM; } break; case PAM_CONV: *item = pamh->pam_conversation; break; case PAM_FAIL_DELAY: *item = pamh->fail_delay.delay_fn_ptr; break; default: retval = PAM_BAD_ITEM; } return retval; }
int _pam_dispatch(pam_handle_t *pamh, int flags, int choice) { struct handler *h = NULL; int retval = PAM_SYSTEM_ERR, use_cached_chain; _pam_boolean resumed; IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from a module!?")); goto end; } /* Load all modules, resolve all symbols */ if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "unable to dispatch function"); goto end; } use_cached_chain = _PAM_PLEASE_FREEZE; switch (choice) { case PAM_AUTHENTICATE: h = pamh->handlers.conf.authenticate; break; case PAM_SETCRED: h = pamh->handlers.conf.setcred; use_cached_chain = _PAM_MAY_BE_FROZEN; break; case PAM_ACCOUNT: h = pamh->handlers.conf.acct_mgmt; break; case PAM_OPEN_SESSION: h = pamh->handlers.conf.open_session; break; case PAM_CLOSE_SESSION: h = pamh->handlers.conf.close_session; use_cached_chain = _PAM_MAY_BE_FROZEN; break; case PAM_CHAUTHTOK: h = pamh->handlers.conf.chauthtok; break; default: pam_syslog(pamh, LOG_ERR, "undefined fn choice; %d", choice); retval = PAM_ABORT; goto end; } if (h == NULL) { /* there was no handlers.conf... entry; will use * handlers.other... */ switch (choice) { case PAM_AUTHENTICATE: h = pamh->handlers.other.authenticate; break; case PAM_SETCRED: h = pamh->handlers.other.setcred; break; case PAM_ACCOUNT: h = pamh->handlers.other.acct_mgmt; break; case PAM_OPEN_SESSION: h = pamh->handlers.other.open_session; break; case PAM_CLOSE_SESSION: h = pamh->handlers.other.close_session; break; case PAM_CHAUTHTOK: h = pamh->handlers.other.chauthtok; break; } } /* Did a module return an "incomplete state" last time? */ if (pamh->former.choice != PAM_NOT_STACKED) { if (pamh->former.choice != choice) { pam_syslog(pamh, LOG_ERR, "application failed to re-exec stack [%d:%d]", pamh->former.choice, choice); retval = PAM_ABORT; goto end; } resumed = PAM_TRUE; } else { resumed = PAM_FALSE; _pam_clear_grantors(h); } __PAM_TO_MODULE(pamh); /* call the list of module functions */ pamh->choice = choice; retval = _pam_dispatch_aux(pamh, flags, h, resumed, use_cached_chain); resumed = PAM_FALSE; __PAM_TO_APP(pamh); /* Should we recall where to resume next time? */ if (retval == PAM_INCOMPLETE) { D(("module [%d] returned PAM_INCOMPLETE")); pamh->former.choice = choice; } else { pamh->former.choice = PAM_NOT_STACKED; } end: #ifdef HAVE_LIBAUDIT if (choice != PAM_CHAUTHTOK || flags & PAM_UPDATE_AUTHTOK || retval != PAM_SUCCESS) { retval = _pam_auditlog(pamh, choice, retval, flags, h); } #endif return retval; }
static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h, _pam_boolean resumed, int use_cached_chain) { int depth, impression, status, skip_depth, prev_level, stack_level; struct _pam_substack_state *substates = NULL; IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR); if (h == NULL) { const void *service=NULL; (void) pam_get_item(pamh, PAM_SERVICE, &service); pam_syslog(pamh, LOG_ERR, "no modules loaded for `%s' service", service ? (const char *)service:"<unknown>" ); service = NULL; return PAM_MUST_FAIL_CODE; } /* if we are recalling this module stack because a former call did not complete, we restore the state of play from pamh. */ if (resumed) { skip_depth = pamh->former.depth; status = pamh->former.status; impression = pamh->former.impression; substates = pamh->former.substates; /* forget all that */ pamh->former.impression = _PAM_UNDEF; pamh->former.status = PAM_MUST_FAIL_CODE; pamh->former.depth = 0; pamh->former.substates = NULL; } else { skip_depth = 0; substates = malloc(PAM_SUBSTACK_MAX_LEVEL * sizeof(*substates)); if (substates == NULL) { pam_syslog(pamh, LOG_CRIT, "_pam_dispatch_aux: no memory for substack states"); return PAM_BUF_ERR; } substates[0].impression = impression = _PAM_UNDEF; substates[0].status = status = PAM_MUST_FAIL_CODE; } prev_level = 0; /* Loop through module logic stack */ for (depth=0 ; h != NULL ; prev_level = stack_level, h = h->next, ++depth) { int retval, cached_retval, action; stack_level = h->stack_level; /* skip leading modules if they have already returned */ if (depth < skip_depth) { continue; } /* remember state if we are entering a substack */ if (prev_level < stack_level) { substates[stack_level].impression = impression; substates[stack_level].status = status; } /* attempt to call the module */ if (h->handler_type == PAM_HT_MUST_FAIL) { D(("module poorly listed in PAM config; forcing failure")); retval = PAM_MUST_FAIL_CODE; } else if (h->handler_type == PAM_HT_SUBSTACK) { D(("skipping substack handler")); continue; } else if (h->func == NULL) { D(("module function is not defined, indicating failure")); retval = PAM_MODULE_UNKNOWN; } else { D(("passing control to module...")); pamh->mod_name=h->mod_name; pamh->mod_argc = h->argc; pamh->mod_argv = h->argv; retval = h->func(pamh, flags, h->argc, h->argv); pamh->mod_name=NULL; pamh->mod_argc = 0; pamh->mod_argv = NULL; D(("module returned: %s", pam_strerror(pamh, retval))); } /* * PAM_INCOMPLETE return is special. It indicates that the * module wants to wait for the application before continuing. * In order to return this, the module will have saved its * state so it can resume from an equivalent position when it * is called next time. (This was added as of 0.65) */ if (retval == PAM_INCOMPLETE) { pamh->former.impression = impression; pamh->former.status = status; pamh->former.depth = depth; pamh->former.substates = substates; D(("module %d returned PAM_INCOMPLETE", depth)); return retval; } /* * use_cached_chain is how we ensure that the setcred and * close_session modules are called in the same order as they did * when they were invoked as auth/open_session. This feature was * added in 0.75 to make the behavior of pam_setcred sane. */ if (use_cached_chain != _PAM_PLEASE_FREEZE) { /* a former stack execution should have frozen the chain */ cached_retval = *(h->cached_retval_p); if (cached_retval == _PAM_INVALID_RETVAL) { /* This may be a problem condition. It implies that the application is running setcred, close_session, chauthtok(2nd) without having first run authenticate, open_session, chauthtok(1st) [respectively]. */ D(("use_cached_chain is set to [%d]," " but cached_retval == _PAM_INVALID_RETVAL", use_cached_chain)); /* In the case of close_session and setcred there is a backward compatibility reason for allowing this, in the chauthtok case we have encountered a bug in libpam! */ if (use_cached_chain == _PAM_MAY_BE_FROZEN) { /* (not ideal) force non-frozen stack control. */ cached_retval = retval; } else { D(("BUG in libpam -" " chain is required to be frozen but isn't")); /* cached_retval is already _PAM_INVALID_RETVAL */ } } } else { /* this stack execution is defining the frozen chain */ cached_retval = h->cached_retval = retval; } /* verify that the return value is a valid one */ if ((cached_retval < PAM_SUCCESS) || (cached_retval >= _PAM_RETURN_VALUES)) { retval = PAM_MUST_FAIL_CODE; action = _PAM_ACTION_BAD; } else { /* We treat the current retval with some respect. It may (for example, in the case of setcred) have a value that needs to be propagated to the user. We want to use the cached_retval to determine the modules to be executed in the stacked chain, but we want to treat each non-ignored module in the cached chain as now being 'required'. We only need to treat the, _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and _PAM_ACTION_RESET actions specially. */ action = h->actions[cached_retval]; } D(("use_cached_chain=%d action=%d cached_retval=%d retval=%d", use_cached_chain, action, cached_retval, retval)); /* decide what to do */ switch (action) { case _PAM_ACTION_RESET: impression = substates[stack_level].impression; status = substates[stack_level].status; break; case _PAM_ACTION_OK: case _PAM_ACTION_DONE: if ( impression == _PAM_UNDEF || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { /* in case of using cached chain we could get here with PAM_IGNORE - don't return it */ if ( retval != PAM_IGNORE || cached_retval == retval ) { impression = _PAM_POSITIVE; status = retval; } } if ( impression == _PAM_POSITIVE ) { if ( retval == PAM_SUCCESS ) { h->grantor = 1; } if ( action == _PAM_ACTION_DONE ) { goto decision_made; } } break; case _PAM_ACTION_BAD: case _PAM_ACTION_DIE: #ifdef PAM_FAIL_NOW_ON if ( cached_retval == PAM_ABORT ) { impression = _PAM_NEGATIVE; status = PAM_PERM_DENIED; goto decision_made; } #endif /* PAM_FAIL_NOW_ON */ if ( impression != _PAM_NEGATIVE ) { impression = _PAM_NEGATIVE; /* Don't return with PAM_IGNORE as status */ if ( retval == PAM_IGNORE ) status = PAM_MUST_FAIL_CODE; else status = retval; } if ( action == _PAM_ACTION_DIE ) { goto decision_made; } break; case _PAM_ACTION_IGNORE: break; /* if we get here, we expect action is a positive number -- this is what the ...JUMP macro checks. */ default: if ( _PAM_ACTION_IS_JUMP(action) ) { /* If we are evaluating a cached chain, we treat this module as required (aka _PAM_ACTION_OK) as well as executing the jump. */ if (use_cached_chain) { if (impression == _PAM_UNDEF || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { if ( retval != PAM_IGNORE || cached_retval == retval ) { if ( impression == _PAM_UNDEF && retval == PAM_SUCCESS ) { h->grantor = 1; } impression = _PAM_POSITIVE; status = retval; } } } /* this means that we need to skip #action stacked modules */ while (h->next != NULL && h->next->stack_level >= stack_level && action > 0) { do { h = h->next; ++depth; } while (h->next != NULL && h->next->stack_level > stack_level); --action; } /* note if we try to skip too many modules action is still non-zero and we snag the next if. */ } /* this case is a syntax error: we can't succeed */ if (action) { pam_syslog(pamh, LOG_ERR, "bad jump in stack"); impression = _PAM_NEGATIVE; status = PAM_MUST_FAIL_CODE; } } continue; decision_made: /* by getting here we have made a decision */ while (h->next != NULL && h->next->stack_level >= stack_level) { h = h->next; ++depth; } } /* Sanity check */ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) { D(("caught on sanity check -- this is probably a config error!")); status = PAM_MUST_FAIL_CODE; } free(substates); /* We have made a decision about the modules executed */ return status; }
int pam_end(pam_handle_t *pamh, int pam_status) { int ret; D(("entering pam_end()")); IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } /* first liberate the modules (it is not inconcevible that the modules may need to use the service_name etc. to clean up) */ _pam_free_data(pamh, pam_status); /* now drop all modules */ if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) { return ret; /* error occurred */ } /* from this point we cannot call the modules any more. Free the remaining memory used by the Linux-PAM interface */ _pam_drop_env(pamh); /* purge the environment */ _pam_overwrite(pamh->authtok); /* blank out old token */ _pam_drop(pamh->authtok); _pam_overwrite(pamh->oldauthtok); /* blank out old token */ _pam_drop(pamh->oldauthtok); _pam_overwrite(pamh->former.prompt); _pam_drop(pamh->former.prompt); /* drop saved prompt */ _pam_overwrite(pamh->service_name); _pam_drop(pamh->service_name); _pam_overwrite(pamh->user); _pam_drop(pamh->user); _pam_overwrite(pamh->prompt); _pam_drop(pamh->prompt); /* prompt for pam_get_user() */ _pam_overwrite(pamh->tty); _pam_drop(pamh->tty); _pam_overwrite(pamh->rhost); _pam_drop(pamh->rhost); _pam_overwrite(pamh->ruser); _pam_drop(pamh->ruser); _pam_drop(pamh->pam_conversation); pamh->fail_delay.delay_fn_ptr = NULL; /* and finally liberate the memory for the pam_handle structure */ _pam_drop(pamh); D(("exiting pam_end() successfully")); return PAM_SUCCESS; }
int pam_get_item (const pam_handle_t *pamh, int item_type, const void **item) { int retval = PAM_SUCCESS; D(("called.")); IF_NO_PAMH("pam_get_item", pamh, PAM_SYSTEM_ERR); if (item == NULL) { pam_syslog(pamh, LOG_ERR, "pam_get_item: nowhere to place requested item"); return PAM_PERM_DENIED; } else *item = NULL; switch (item_type) { case PAM_SERVICE: *item = pamh->service_name; break; case PAM_USER: D(("returning user=%s", pamh->user)); *item = pamh->user; break; case PAM_USER_PROMPT: D(("returning userprompt=%s", pamh->user)); *item = pamh->prompt; break; case PAM_TTY: D(("returning tty=%s", pamh->tty)); *item = pamh->tty; break; case PAM_RUSER: *item = pamh->ruser; break; case PAM_RHOST: *item = pamh->rhost; break; case PAM_AUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { *item = pamh->authtok; } else { retval = PAM_BAD_ITEM; } break; case PAM_OLDAUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { *item = pamh->oldauthtok; } else { retval = PAM_BAD_ITEM; } break; case PAM_CONV: *item = pamh->pam_conversation; break; case PAM_FAIL_DELAY: *item = pamh->fail_delay.delay_fn_ptr; break; case PAM_XDISPLAY: *item = pamh->xdisplay; break; case PAM_XAUTHDATA: *item = &pamh->xauth; break; /* begin: add by yangguang */ default: if (item_type > PAM_EXT_BASE) { retval = pam_get_extern_item_value (pamh, item_type, item); } else { retval = PAM_BAD_ITEM; } /* end: add by yangguang */ }/* end switch */ return retval; }
int pam_authenticate(pam_handle_t *pamh, int flags) { int retval; D(("pam_authenticate called")); IF_NO_PAMH("pam_authenticate", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } if (pamh->former.choice == PAM_NOT_STACKED) { _pam_sanitize(pamh); _pam_start_timer(pamh); /* we try to make the time for a failure independent of the time it takes to fail */ } retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE); if (retval != PAM_INCOMPLETE) { _pam_sanitize(pamh); _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ D(("pam_authenticate exit")); } else { D(("will resume when ready")); } #ifdef PRELUDE prelude_send_alert(pamh, retval); #endif #if HAVE_LIBAUDIT retval = _pam_auditlog(pamh, PAM_AUTHENTICATE, retval, flags); #endif #ifdef CONFIG_PROP_STATSD_STATSD if (retval != PAM_SUCCESS) { char usr[MAX_PAM_STATS_USR_SIZE]; char buf[MAX_PAM_STATS_BUF_SIZE]; struct pam_data *data; char *u = NULL; /* The pam_sg module has stored module data so we * can tell whether this is a valid user. If not * we log stats under "unknown". The proper mechanism * for accessing module data bars access from within * application code so we are going around it. This is * a kludge, but the best one possible for now. */ data = pamh->data; while (data) { if (!strcmp(data->name, pamh->user)) { u = (char *)(data->data); break; } data = data->next; } /* Don't log stats if the module info is unavailable * or the PAM system itself failed during auth */ if ((u != NULL) && strcmp(u, "PAM_SYSTEM_ERR")) { u = ((u != NULL) && !strcmp(u, "PAM_USER_UNKNOWN")) ? "unknown":pamh->user; //u = ((u != NULL) && !strcmp(u, "USER_NOTFOUND")) ? "unknown":pamh->user; usr[MAX_PAM_STATS_USR_SIZE-1]='\0'; strncpy(usr,u,MAX_PAM_STATS_USR_SIZE-1); /* OK, start logging stats */ memset(buf,'\0',MAX_PAM_STATS_BUF_SIZE); snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_failed_%s %s", usr,pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s failed", buf); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd push pam_last_failure_%s %s \"%s\" 0", usr,pamh->service_name, pam_strerror(pamh, retval)); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s failed", buf); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_users %s",usr); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_services %s",pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } } } #endif return retval; }
int pam_set_item (pam_handle_t *pamh, int item_type, const void *item) { int retval; D(("called")); IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR); retval = PAM_SUCCESS; switch (item_type) { case PAM_SERVICE: /* Setting handlers_loaded to 0 will cause the handlers * to be reloaded on the next call to a service module. */ pamh->handlers.handlers_loaded = 0; TRY_SET(pamh->service_name, item); { char *tmp; for (tmp=pamh->service_name; *tmp; ++tmp) *tmp = tolower(*tmp); /* require lower case */ } break; case PAM_USER: TRY_SET(pamh->user, item); pamh->former.fail_user = PAM_SUCCESS; break; case PAM_USER_PROMPT: TRY_SET(pamh->prompt, item); pamh->former.fail_user = PAM_SUCCESS; break; case PAM_TTY: D(("setting tty to %s", item)); TRY_SET(pamh->tty, item); break; case PAM_RUSER: TRY_SET(pamh->ruser, item); break; case PAM_RHOST: TRY_SET(pamh->rhost, item); break; case PAM_AUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { if (pamh->authtok != item) { _pam_overwrite(pamh->authtok); TRY_SET(pamh->authtok, item); } } else { retval = PAM_BAD_ITEM; } break; case PAM_OLDAUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { if (pamh->oldauthtok != item) { _pam_overwrite(pamh->oldauthtok); TRY_SET(pamh->oldauthtok, item); } } else { retval = PAM_BAD_ITEM; } break; case PAM_CONV: /* want to change the conversation function */ if (item == NULL) { pam_syslog(pamh, LOG_ERR, "pam_set_item: attempt to set conv() to NULL"); retval = PAM_PERM_DENIED; } else { struct pam_conv *tconv; if ((tconv= (struct pam_conv *) malloc(sizeof(struct pam_conv)) ) == NULL) { pam_syslog(pamh, LOG_CRIT, "pam_set_item: malloc failed for pam_conv"); retval = PAM_BUF_ERR; } else { memcpy(tconv, item, sizeof(struct pam_conv)); _pam_drop(pamh->pam_conversation); pamh->pam_conversation = tconv; pamh->former.fail_user = PAM_SUCCESS; } } break; case PAM_FAIL_DELAY: pamh->fail_delay.delay_fn_ptr = item; break; case PAM_XDISPLAY: TRY_SET(pamh->xdisplay, item); break; case PAM_XAUTHDATA: if (&pamh->xauth == item) break; if (pamh->xauth.namelen) { _pam_overwrite(pamh->xauth.name); free(pamh->xauth.name); } if (pamh->xauth.datalen) { _pam_overwrite_n(pamh->xauth.data, (unsigned int) pamh->xauth.datalen); free(pamh->xauth.data); } pamh->xauth = *((const struct pam_xauth_data *) item); if ((pamh->xauth.name=_pam_strdup(pamh->xauth.name)) == NULL) { memset(&pamh->xauth, '\0', sizeof(pamh->xauth)); return PAM_BUF_ERR; } if ((pamh->xauth.data=_pam_memdup(pamh->xauth.data, pamh->xauth.datalen)) == NULL) { _pam_overwrite(pamh->xauth.name); free(pamh->xauth.name); memset(&pamh->xauth, '\0', sizeof(pamh->xauth)); return PAM_BUF_ERR; } break; /* begin: add by yangguang */ default: if (item_type > PAM_EXT_BASE) { retval = pam_set_extern_item_value (pamh, item_type, item); } else { retval = PAM_BAD_ITEM; } }/* end switch */ /* end: add by yangguang */ return retval; }
int pam_end(pam_handle_t *pamh, int pam_status) { int ret; D(("entering pam_end()")); IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } #ifdef HAVE_LIBAUDIT _pam_audit_end(pamh, pam_status); #endif #ifdef CONFIG_PROP_STATSD_STATSD if (pam_status == PAM_SUCCESS) { char buf[MAX_PAM_STATS_BUF_SIZE]; memset(buf,'\0',MAX_PAM_STATS_BUF_SIZE); snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_succeeded_%s %s", pamh->user,pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s %s statsd incr failed", buf, pamh->service_name); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_users %s", pamh->user); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_services %s", pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } } #endif /* first liberate the modules (it is not inconcevible that the modules may need to use the service_name etc. to clean up) */ _pam_free_data(pamh, pam_status); /* now drop all modules */ if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) { return ret; /* error occurred */ } /* from this point we cannot call the modules any more. Free the remaining memory used by the Linux-PAM interface */ _pam_drop_env(pamh); /* purge the environment */ _pam_overwrite(pamh->authtok); /* blank out old token */ _pam_drop(pamh->authtok); _pam_overwrite(pamh->oldauthtok); /* blank out old token */ _pam_drop(pamh->oldauthtok); _pam_overwrite(pamh->former.prompt); _pam_drop(pamh->former.prompt); /* drop saved prompt */ _pam_overwrite(pamh->service_name); _pam_drop(pamh->service_name); _pam_overwrite(pamh->user); _pam_drop(pamh->user); _pam_overwrite(pamh->prompt); _pam_drop(pamh->prompt); /* prompt for pam_get_user() */ _pam_overwrite(pamh->tty); _pam_drop(pamh->tty); _pam_overwrite(pamh->rhost); _pam_drop(pamh->rhost); _pam_overwrite(pamh->ruser); _pam_drop(pamh->ruser); _pam_drop(pamh->pam_conversation); pamh->fail_delay.delay_fn_ptr = NULL; /* and finally liberate the memory for the pam_handle structure */ _pam_drop(pamh); D(("exiting pam_end() successfully")); return PAM_SUCCESS; }
static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h, _pam_boolean resumed, int use_cached_chain) { int depth, impression, status, skip_depth; IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR); if (h == NULL) { const char *service=NULL; (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service); _pam_system_log(LOG_ERR, "no modules loaded for `%s' service", service ? service:"<unknown>" ); service = NULL; return PAM_MUST_FAIL_CODE; } /* if we are recalling this module stack because a former call did not complete, we restore the state of play from pamh. */ if (resumed) { skip_depth = pamh->former.depth; status = pamh->former.status; impression = pamh->former.impression; /* forget all that */ pamh->former.impression = _PAM_UNDEF; pamh->former.status = PAM_MUST_FAIL_CODE; pamh->former.depth = 0; } else { skip_depth = 0; impression = _PAM_UNDEF; status = PAM_MUST_FAIL_CODE; } /* Loop through module logic stack */ for (depth=0 ; h != NULL ; h = h->next, ++depth) { int retval, cached_retval, action; /* skip leading modules if they have already returned */ if (depth < skip_depth) { continue; } /* attempt to call the module */ if (h->func == NULL) { D(("module function is not defined, indicating failure")); retval = PAM_MODULE_UNKNOWN; } else { D(("passing control to module...")); retval = h->func(pamh, flags, h->argc, h->argv); D(("module returned: %s", pam_strerror(pamh, retval))); if (h->must_fail) { D(("module poorly listed in PAM config; forcing failure")); retval = PAM_MUST_FAIL_CODE; } } /* * PAM_INCOMPLETE return is special. It indicates that the * module wants to wait for the application before continuing. * In order to return this, the module will have saved its * state so it can resume from an equivalent position when it * is called next time. (This was added as of 0.65) */ if (retval == PAM_INCOMPLETE) { pamh->former.impression = impression; pamh->former.status = status; pamh->former.depth = depth; D(("module %d returned PAM_INCOMPLETE", depth)); return retval; } if (use_cached_chain) { /* a former stack execution has frozen the chain */ cached_retval = *(h->cached_retval_p); } else { /* this stack execution is defining the frozen chain */ cached_retval = h->cached_retval = retval; } /* verify that the return value is a valid one */ if ((cached_retval < PAM_SUCCESS) || (cached_retval >= _PAM_RETURN_VALUES)) { retval = PAM_MUST_FAIL_CODE; action = _PAM_ACTION_BAD; } else { /* We treat the current retval with some respect. It may (for example, in the case of setcred) have a value that needs to be propagated to the user. We want to use the cached_retval to determine the modules to be executed in the stacked chain, but we want to treat each non-ignored module in the cached chain as now being 'required'. We only need to treat the, _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and _PAM_ACTION_RESET actions specially. */ action = h->actions[cached_retval]; } D((stderr, "use_cached_chain=%d action=%d cached_retval=%d retval=%d\n", use_cached_chain, action, cached_retval, retval)); /* decide what to do */ switch (action) { case _PAM_ACTION_RESET: /* if (use_cached_chain) { XXX - we need to consider the use_cached_chain case do we want to trash accumulated info here..? } */ impression = _PAM_UNDEF; status = PAM_MUST_FAIL_CODE; break; case _PAM_ACTION_OK: case _PAM_ACTION_DONE: /* XXX - should we maintain cached_status and status in the case of use_cached_chain? The same with BAD&DIE below */ if ( impression == _PAM_UNDEF || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { impression = _PAM_POSITIVE; status = retval; } if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) { goto decision_made; } break; case _PAM_ACTION_BAD: case _PAM_ACTION_DIE: #ifdef PAM_FAIL_NOW_ON if ( cached_retval == PAM_ABORT ) { impression = _PAM_NEGATIVE; status = PAM_PERM_DENIED; goto decision_made; } #endif /* PAM_FAIL_NOW_ON */ if ( impression != _PAM_NEGATIVE ) { impression = _PAM_NEGATIVE; status = retval; } if ( action == _PAM_ACTION_DIE ) { goto decision_made; } break; case _PAM_ACTION_IGNORE: /* if (use_cached_chain) { XXX - when evaluating a cached chain, do we still want to ignore the module's return value? } */ break; /* if we get here, we expect action is a positive number -- this is what the ...JUMP macro checks. */ default: if ( _PAM_ACTION_IS_JUMP(action) ) { /* If we are evaluating a cached chain, we treat this module as required (aka _PAM_ACTION_OK) as well as executing the jump. */ if (use_cached_chain) { if (impression == _PAM_UNDEF || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { impression = _PAM_POSITIVE; status = retval; } } /* this means that we need to skip #action stacked modules */ do { h = h->next; } while ( --action > 0 && h != NULL ); /* note if we try to skip too many modules action is still non-zero and we snag the next if. */ } /* this case is a syntax error: we can't succeed */ if (action) { D(("action syntax error")); impression = _PAM_NEGATIVE; status = PAM_MUST_FAIL_CODE; } } } decision_made: /* by getting here we have made a decision */ /* Sanity check */ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) { D(("caught on sanity check -- this is probably a config error!")); status = PAM_MUST_FAIL_CODE; } /* We have made a decision about the modules executed */ return status; }
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; }