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; } /* 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_system_log(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_system_log(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_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; }
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_dispatch(pam_handle_t *pamh, int flags, int choice) { struct handler *h = NULL; int retval, 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!?")); return PAM_SYSTEM_ERR; } /* Load all modules, resolve all symbols */ if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) { _pam_system_log(LOG_ERR, "unable to dispatch function"); return retval; } use_cached_chain = 0; /* default to setting h->cached_retval */ switch (choice) { case PAM_AUTHENTICATE: h = pamh->handlers.conf.authenticate; break; case PAM_SETCRED: h = pamh->handlers.conf.setcred; use_cached_chain = 1; 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 = 1; break; case PAM_CHAUTHTOK: h = pamh->handlers.conf.chauthtok; if (flags & PAM_UPDATE_AUTHTOK) { use_cached_chain = 1; } break; default: _pam_system_log(LOG_ERR, "undefined fn choice; %d", choice); return PAM_ABORT; } 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_system_log(LOG_ERR, "application failed to re-exec stack [%d:%d]", pamh->former.choice, choice); return PAM_ABORT; } resumed = PAM_TRUE; } else { resumed = PAM_FALSE; } __PAM_TO_MODULE(pamh); /* call the list of module functions */ 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; } return retval; }