summaryrefslogtreecommitdiffstats
path: root/libpam/pam_item.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libpam/pam_item.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/libpam/pam_item.c b/libpam/pam_item.c
new file mode 100644
index 0000000..d6af710
--- /dev/null
+++ b/libpam/pam_item.c
@@ -0,0 +1,396 @@
+/* pam_item.c */
+
+/*
+ * $Id$
+ */
+
+#include "pam_private.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#define TRY_SET(X, Y) \
+{ \
+ if ((X) != (Y)) { \
+ char *_TMP_ = _pam_strdup(Y); \
+ if (_TMP_ == NULL && (Y) != NULL) \
+ return PAM_BUF_ERR; \
+ free(X); \
+ (X) = _TMP_; \
+ } \
+}
+
+/* functions */
+
+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;
+
+ case PAM_AUTHTOK_TYPE:
+ TRY_SET(pamh->authtok_type, item);
+ break;
+
+ default:
+ retval = PAM_BAD_ITEM;
+ }
+
+ return retval;
+}
+
+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;
+
+ case PAM_AUTHTOK_TYPE:
+ *item = pamh->authtok_type;
+ break;
+
+ default:
+ retval = PAM_BAD_ITEM;
+ }
+
+ return retval;
+}
+
+/*
+ * This function is the 'preferred method to obtain the username'.
+ */
+
+int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt)
+{
+ const char *use_prompt;
+ int retval;
+ struct pam_message msg;
+ const struct pam_message *pmsg;
+ struct pam_response *resp;
+
+ D(("called."));
+
+ IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR);
+
+ if (user == NULL) {
+ /* ensure that the module has supplied a destination */
+ pam_syslog(pamh, LOG_ERR, "pam_get_user: nowhere to record username");
+ return PAM_SYSTEM_ERR;
+ } else
+ *user = NULL;
+
+ if (pamh->pam_conversation == NULL) {
+ pam_syslog(pamh, LOG_ERR, "pam_get_user: no conv element in pamh");
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (pamh->user) { /* have one so return it */
+ *user = pamh->user;
+ return PAM_SUCCESS;
+ }
+
+ if (pamh->former.fail_user != PAM_SUCCESS)
+ return pamh->former.fail_user;
+
+ /* will need a prompt */
+ if (prompt != NULL)
+ use_prompt = prompt;
+ else if (pamh->prompt != NULL)
+ use_prompt = pamh->prompt;
+ else
+ use_prompt = _("login:");
+
+ /* 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_syslog(pamh, 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_syslog(pamh, 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, &pmsg, &resp, pamh->pam_conversation->appdata_ptr);
+
+ switch (retval) {
+ case PAM_SUCCESS:
+ case PAM_BUF_ERR:
+ case PAM_CONV_AGAIN:
+ case PAM_CONV_ERR:
+ break;
+ default:
+ retval = PAM_CONV_ERR;
+ }
+
+ switch (retval) {
+ case 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);
+ break;
+ case PAM_SUCCESS:
+ if (resp != NULL && resp->resp != NULL) {
+ /*
+ * 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.
+ */
+ retval = pam_set_item(pamh, PAM_USER, resp->resp);
+ *user = pamh->user;
+ break;
+ } else {
+ /* conversation should have given a response */
+ D(("pam_get_user: no response provided"));
+ retval = PAM_CONV_ERR;
+ }
+ /* fallthrough */
+ default:
+ pamh->former.fail_user = retval;
+ }
+
+ if (resp) {
+ if (retval != PAM_SUCCESS)
+ pam_syslog(pamh, LOG_WARNING,
+ "unexpected response from failed conversation function");
+ /*
+ * 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 */
+}