コード例 #1
0
/* Ask the application to display a short text string for us. */
static int make_remark(pam_handle_t * pamh, int ctrl, const char *remark)
{
   int retval;

   if ((ctrl & MKHOMEDIR_QUIET) != MKHOMEDIR_QUIET)
   {
      struct pam_message msg[1], *mesg[1];
      struct pam_response *resp = NULL;

      mesg[0] = &msg[0];
      msg[0].msg_style = PAM_TEXT_INFO;
      msg[0].msg = remark;

      retval = converse(pamh, ctrl, 1, mesg, &resp);

      msg[0].msg = NULL;
      if (resp)
      {
	 _pam_drop_reply(resp, 1);
      }
   }
   else
   {
      D(("keeping quiet"));
      retval = PAM_SUCCESS;
   }

   D(("returning %s", pam_strerror(pamh, retval)));
   return retval;
}
コード例 #2
0
PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
                        const char **argv)
{
     int retval = PAM_SUCCESS;
     int fd;
     const char *username;
     char *mtmp=NULL;
     struct passwd *user_pwd;
     struct pam_conv *conversation;
     struct pam_message message;
     struct pam_message *pmessage = &message;
     struct pam_response *resp = NULL;
     struct stat st;

     if ((fd = open("/etc/nologin", O_RDONLY, 0)) >= 0) {
       /* root can still log in; lusers cannot */
       if ((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS)
           || !username) {
         return PAM_SERVICE_ERR;
       }
       user_pwd = getpwnam(username);
       if (user_pwd && user_pwd->pw_uid == 0) {
         message.msg_style = PAM_TEXT_INFO;
       } else {
	   if (!user_pwd) {
	       retval = PAM_USER_UNKNOWN;
	   } else {
	       retval = PAM_AUTH_ERR;
	   }
	   message.msg_style = PAM_ERROR_MSG;
       }

       /* fill in message buffer with contents of /etc/nologin */
       if (fstat(fd, &st) < 0) /* give up trying to display message */
         return retval;
       message.msg = mtmp = malloc(st.st_size+1);
       /* if malloc failed... */
       if (!message.msg) return retval;
       read(fd, mtmp, st.st_size);
       mtmp[st.st_size] = '\000';

       /* Use conversation function to give user contents of /etc/nologin */
       pam_get_item(pamh, PAM_CONV, (const void **)&conversation);
       conversation->conv(1, (const struct pam_message **)&pmessage,
			  &resp, conversation->appdata_ptr);
       free(mtmp);
       if (resp)
	   _pam_drop_reply(resp, 1);
     }

     return retval;
}
コード例 #3
0
ファイル: pam_mail.c プロジェクト: aosm/pam
static int report_mail(pam_handle_t *pamh, int ctrl
		       , const char *type, const char *folder)
{
    int retval;

    if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && strcmp(type, "new"))) {
	char *remark;

	if (ctrl & PAM_STANDARD_MAIL)
	    if (!strcmp(type, "no"))
		remark = malloc(strlen(NO_MAIL_STANDARD_FORMAT)+1);
	    else
		remark = malloc(strlen(YOUR_MAIL_STANDARD_FORMAT)+strlen(type)+1);
	else
	    remark = malloc(strlen(YOUR_MAIL_VERBOSE_FORMAT)+strlen(type)+strlen(folder)+1);
	if (remark == NULL) {
	    retval = PAM_BUF_ERR;
	} else {
	    struct pam_message msg[1], *mesg[1];
	    struct pam_response *resp=NULL;

	    if (ctrl & PAM_STANDARD_MAIL)
		if (!strcmp(type, "no"))
		    sprintf(remark, NO_MAIL_STANDARD_FORMAT);
		else
		    sprintf(remark, YOUR_MAIL_STANDARD_FORMAT, type);
	    else
		sprintf(remark, YOUR_MAIL_VERBOSE_FORMAT, type, folder);

	    mesg[0] = &msg[0];
	    msg[0].msg_style = PAM_TEXT_INFO;
	    msg[0].msg = remark;

	    retval = converse(pamh, ctrl, 1, mesg, &resp);

	    _pam_overwrite(remark);
	    _pam_drop(remark);
	    if (resp)
		_pam_drop_reply(resp, 1);
	}
    } else {
	D(("keeping quiet"));
	retval = PAM_SUCCESS;
    }

    D(("returning %s", pam_strerror(pamh, retval)));
    return retval;
}
コード例 #4
0
ファイル: pam.cpp プロジェクト: greyson/pam_couchdb
static int conversation( pam_handle_t * pamh )
{
   struct pam_message msg[2], *pmsg[2];
   struct pam_response * resp;

   int retval;
   char * token = NULL;

   pmsg[0] = &msg[0];
   msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
   msg[0].msg = "Password: ";

   // Call the conversation expecting 1 response
   resp = NULL;
   retval = converse( pamh, pmsg, &resp );

   if( resp != NULL )
   {
      void const * item;

      // interpret the response
      if( retval == PAM_SUCCESS )
      {
         token = x_strdup( resp[0].resp );
         if( token == NULL )
         {
            return PAM_AUTHTOK_RECOVER_ERR;
         }
      }

      // Set the auth token
      retval = pam_set_item( pamh, PAM_AUTHTOK, token );
      token = _pam_delete( token );
      if( retval != PAM_SUCCESS ||
          (retval = pam_get_item( pamh, PAM_AUTHTOK, & item )) != PAM_SUCCESS )
      {
         return retval;
      }

      _pam_drop_reply( resp, 1 );
   }
   else if( retval == PAM_SUCCESS )
   {
      retval = PAM_AUTHTOK_RECOVER_ERR;
   }

   return retval;
}
コード例 #5
0
ファイル: pam_cracklib.c プロジェクト: aosm/pam
static int make_remark(pam_handle_t *pamh, unsigned int ctrl,
                       int type, const char *text)
{
    struct pam_message *pmsg[1], msg[1];
    struct pam_response *resp;
    int retval;

    pmsg[0] = &msg[0];
    msg[0].msg = text;
    msg[0].msg_style = type;
    resp = NULL;

    retval = converse(pamh, ctrl, 1, pmsg, &resp);
    if (retval == PAM_SUCCESS)
	_pam_drop_reply(resp, 1);

    return retval;
}
コード例 #6
0
ファイル: pam_winbind.c プロジェクト: hajuuk/R7000
static int _make_remark(pam_handle_t * pamh, int type, const char *text)
{
	int retval = PAM_SUCCESS;

	struct pam_message *pmsg[1], msg[1];
	struct pam_response *resp;
	
	pmsg[0] = &msg[0];
	msg[0].msg = text;
	msg[0].msg_style = type;
	
	resp = NULL;
	retval = converse(pamh, 1, pmsg, &resp);
	
	if (resp) {
		_pam_drop_reply(resp, 1);
	}
	return retval;
}
コード例 #7
0
ファイル: pam_item.c プロジェクト: Claruarius/stblinux-2.6.37
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 */
}
コード例 #8
0
ファイル: pam_passwdqc.c プロジェクト: 2014-class/freerouter
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
    int argc, const char **argv)
{
	params_t params;
	struct pam_response *resp;
	struct passwd *pw, fake_pw;
#ifdef HAVE_SHADOW
	struct spwd *spw;
#endif
	char *user, *oldpass, *newpass, *randompass;
	const char *reason;
	int ask_oldauthtok;
	int randomonly, enforce, retries_left, retry_wanted;
	int status;

	params = defaults;
	status = parse(&params, pamh, argc, argv);
	if (status != PAM_SUCCESS)
		return status;

	ask_oldauthtok = 0;
	if (flags & PAM_PRELIM_CHECK) {
		if (params.flags & F_ASK_OLDAUTHTOK_PRELIM)
			ask_oldauthtok = 1;
	} else
	if (flags & PAM_UPDATE_AUTHTOK) {
		if (params.flags & F_ASK_OLDAUTHTOK_UPDATE)
			ask_oldauthtok = 1;
	} else
		return PAM_SERVICE_ERR;

	if (ask_oldauthtok && getuid() != 0) {
		status = converse(pamh, PAM_PROMPT_ECHO_OFF,
		    PROMPT_OLDPASS, &resp);

		if (status == PAM_SUCCESS) {
			if (resp && resp->resp) {
				status = pam_set_item(pamh,
				    PAM_OLDAUTHTOK, resp->resp);
				_pam_drop_reply(resp, 1);
			} else
				status = PAM_AUTHTOK_RECOVERY_ERR;
		}

		if (status != PAM_SUCCESS)
			return status;
	}

	if (flags & PAM_PRELIM_CHECK)
		return status;

	status = pam_get_item(pamh, PAM_USER, (pam_item_t *)&user);
	if (status != PAM_SUCCESS)
		return status;

	status = pam_get_item(pamh, PAM_OLDAUTHTOK, (pam_item_t *)&oldpass);
	if (status != PAM_SUCCESS)
		return status;

	if (params.flags & F_NON_UNIX) {
		pw = &fake_pw;
		pw->pw_name = user;
		pw->pw_gecos = "";
	} else {
		pw = getpwnam(user);
		endpwent();
		if (!pw)
			return PAM_USER_UNKNOWN;
		if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) {
			if (!oldpass)
				status = PAM_AUTH_ERR;
			else
#ifdef HAVE_SHADOW
			if (!strcmp(pw->pw_passwd, "x")) {
				spw = getspnam(user);
				endspent();
				if (spw) {
					if (strcmp(crypt(oldpass, spw->sp_pwdp),
					    spw->sp_pwdp))
						status = PAM_AUTH_ERR;
					memset(spw->sp_pwdp, 0,
					    strlen(spw->sp_pwdp));
				} else
					status = PAM_AUTH_ERR;
			} else
#endif
			if (strcmp(crypt(oldpass, pw->pw_passwd),
			    pw->pw_passwd))
				status = PAM_AUTH_ERR;
		}
		memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
		if (status != PAM_SUCCESS)
			return status;
	}

	randomonly = params.qc.min[4] > params.qc.max;

	if (getuid() != 0)
		enforce = params.flags & F_ENFORCE_USERS;
	else
		enforce = params.flags & F_ENFORCE_ROOT;

	if (params.flags & F_USE_AUTHTOK) {
		status = pam_get_item(pamh, PAM_AUTHTOK,
		    (pam_item_t *)&newpass);
		if (status != PAM_SUCCESS)
			return status;
		if (!newpass || (check_max(&params, pamh, newpass) && enforce))
			return PAM_AUTHTOK_ERR;
		reason = _passwdqc_check(&params.qc, newpass, oldpass, pw);
		if (reason) {
			say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
			if (enforce)
				status = PAM_AUTHTOK_ERR;
		}
		return status;
	}

	retries_left = params.retry;

retry:
	retry_wanted = 0;

	if (!randomonly &&
	    params.qc.passphrase_words && params.qc.min[2] <= params.qc.max)
		status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_BOTH);
	else
		status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD);
	if (status != PAM_SUCCESS)
		return status;

	if (!randomonly && params.qc.min[3] <= params.qc.min[4])
		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_1,
		    params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
		    params.qc.min[3]);
	else
	if (!randomonly)
		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2,
		    params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "",
		    params.qc.min[3],
		    params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "",
		    params.qc.min[4]);
	if (status != PAM_SUCCESS)
		return status;

	if (!randomonly &&
	    params.qc.passphrase_words &&
	    params.qc.min[2] <= params.qc.max) {
		status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSPHRASE,
		    params.qc.passphrase_words,
		    params.qc.min[2], params.qc.max);
		if (status != PAM_SUCCESS)
			return status;
	}

	randompass = _passwdqc_random(&params.qc);
	if (randompass) {
		status = say(pamh, PAM_TEXT_INFO, randomonly ?
		    MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass);
		if (status != PAM_SUCCESS) {
			_pam_overwrite(randompass);
			randompass = NULL;
		}
	} else
	if (randomonly) {
		say(pamh, PAM_ERROR_MSG, getuid() != 0 ?
		    MESSAGE_MISCONFIGURED : MESSAGE_RANDOMFAILED);
		return PAM_AUTHTOK_ERR;
	}

	status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS1, &resp);
	if (status == PAM_SUCCESS && (!resp || !resp->resp))
		status = PAM_AUTHTOK_ERR;

	if (status != PAM_SUCCESS) {
		if (randompass) _pam_overwrite(randompass);
		return status;
	}

	newpass = strdup(resp->resp);

	_pam_drop_reply(resp, 1);

	if (!newpass) {
		if (randompass) _pam_overwrite(randompass);
		return PAM_AUTHTOK_ERR;
	}

	if (check_max(&params, pamh, newpass) && enforce) {
		status = PAM_AUTHTOK_ERR;
		retry_wanted = 1;
	}

	reason = NULL;
	if (status == PAM_SUCCESS &&
	    (!randompass || !strstr(newpass, randompass)) &&
	    (randomonly ||
	    (reason = _passwdqc_check(&params.qc, newpass, oldpass, pw)))) {
		if (randomonly)
			say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM);
		else
			say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason);
		if (enforce) {
			status = PAM_AUTHTOK_ERR;
			retry_wanted = 1;
		}
	}

	if (status == PAM_SUCCESS)
		status = converse(pamh, PAM_PROMPT_ECHO_OFF,
		    PROMPT_NEWPASS2, &resp);
	if (status == PAM_SUCCESS) {
		if (resp && resp->resp) {
			if (strcmp(newpass, resp->resp)) {
				status = say(pamh,
				    PAM_ERROR_MSG, MESSAGE_MISTYPED);
				if (status == PAM_SUCCESS) {
					status = PAM_AUTHTOK_ERR;
					retry_wanted = 1;
				}
			}
			_pam_drop_reply(resp, 1);
		} else
			status = PAM_AUTHTOK_ERR;
	}

	if (status == PAM_SUCCESS)
		status = pam_set_item(pamh, PAM_AUTHTOK, newpass);

	if (randompass) _pam_overwrite(randompass);
	_pam_overwrite(newpass);
	free(newpass);

	if (retry_wanted && --retries_left > 0) {
		status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY);
		if (status == PAM_SUCCESS)
			goto retry;
	}

	return status;
}
コード例 #9
0
ファイル: pam_ftp.c プロジェクト: ysleu/RTL8685
PAM_EXTERN
int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc
			,const char **argv)
{
    int retval, anon=0, ctrl;
    const char *user;
    char *users=NULL;

    /*
     * this module checks if the user name is ftp or annonymous. If
     * this is the case, it can set the PAM_RUSER to the entered email
     * address and SUCCEEDS, otherwise it FAILS.
     */

    ctrl = _pam_parse(argc, argv, &users);

    retval = pam_get_user(pamh, &user, NULL);
    if (retval != PAM_SUCCESS || user == NULL) {
	_pam_log(LOG_ERR, "no user specified");
	return PAM_USER_UNKNOWN;
    }

    if (!(ctrl & PAM_NO_ANON)) {
	anon = lookup(user, users, &user);
    }

    if (anon) {
	retval = pam_set_item(pamh, PAM_USER, (const void *)user);
	if (retval != PAM_SUCCESS || user == NULL) {
	    _pam_log(LOG_ERR, "user resetting failed");
	    return PAM_USER_UNKNOWN;
	}
    }

    /*
     * OK. we require an email address for user or the user's password.
     * - build conversation and get their input.
     */

    {
	struct pam_message msg[1], *mesg[1];
	struct pam_response *resp=NULL;
	const char *token;
	char *prompt=NULL;
	int i=0;

	if (!anon) {
	    prompt = malloc(strlen(PLEASE_ENTER_PASSWORD) + strlen(user));
	    if (prompt == NULL) {
		D(("out of memory!?"));
		return PAM_BUF_ERR;
	    } else {
		sprintf(prompt, PLEASE_ENTER_PASSWORD, user);
		msg[i].msg = prompt;
	    }
	} else {
	    msg[i].msg = GUEST_LOGIN_PROMPT;
	}

	msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
	mesg[i] = &msg[i];

	retval = converse(pamh, ++i, mesg, &resp);
	if (prompt) {
	    _pam_overwrite(prompt);
	    _pam_drop(prompt);
	}

	if (retval != PAM_SUCCESS) {
	    if (resp != NULL)
		_pam_drop_reply(resp,i);
	    return ((retval == PAM_CONV_AGAIN)
		    ? PAM_INCOMPLETE:PAM_AUTHINFO_UNAVAIL);
	}

	if (anon) {
	    /* XXX: Some effort should be made to verify this email address! */

	    if (!(ctrl & PAM_IGNORE_EMAIL)) {
		token = strtok(resp->resp, "@");
		retval = pam_set_item(pamh, PAM_RUSER, token);

		if ((token) && (retval == PAM_SUCCESS)) {
		    token = strtok(NULL, "@");
		    retval = pam_set_item(pamh, PAM_RHOST, token);
		}
	    }

	    /* we are happy to grant annonymous access to the user */
	    retval = PAM_SUCCESS;

	} else {
	    /*
	     * we have a password so set AUTHTOK
	     */

	    (void) pam_set_item(pamh, PAM_AUTHTOK, resp->resp);

	    /*
	     * this module failed, but the next one might succeed with
	     * this password.
	     */

	    retval = PAM_AUTH_ERR;
	}

	if (resp) {                                      /* clean up */
	    _pam_drop_reply(resp, i);
	}

	/* success or failure */

	return retval;
    }
}
コード例 #10
0
ファイル: pam_winbind.c プロジェクト: hajuuk/R7000
static int _winbind_read_password(pam_handle_t * pamh
				  ,unsigned int ctrl
				  ,const char *comment
				  ,const char *prompt1
				  ,const char *prompt2
				  ,const char **pass)
{
	int authtok_flag;
	int retval;
	const char *item;
	char *token;

	/*
	 * make sure nothing inappropriate gets returned
	 */

	*pass = token = NULL;

	/*
	 * which authentication token are we getting?
	 */

	authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;

	/*
	 * should we obtain the password from a PAM item ?
	 */

	if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
		retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
		if (retval != PAM_SUCCESS) {
			/* very strange. */
			_pam_log(LOG_ALERT, 
				 "pam_get_item returned error to unix-read-password"
			    );
			return retval;
		} else if (item != NULL) {	/* we have a password! */
			*pass = item;
			item = NULL;
			return PAM_SUCCESS;
		} else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
			return PAM_AUTHTOK_RECOVER_ERR;		/* didn't work */
		} else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
			   && off(WINBIND__OLD_PASSWORD, ctrl)) {
			return PAM_AUTHTOK_RECOVER_ERR;
		}
	}
	/*
	 * getting here implies we will have to get the password from the
	 * user directly.
	 */

	{
		struct pam_message msg[3], *pmsg[3];
		struct pam_response *resp;
		int i, replies;

		/* prepare to converse */

		if (comment != NULL) {
			pmsg[0] = &msg[0];
			msg[0].msg_style = PAM_TEXT_INFO;
			msg[0].msg = comment;
			i = 1;
		} else {
			i = 0;
		}

		pmsg[i] = &msg[i];
		msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
		msg[i++].msg = prompt1;
		replies = 1;

		if (prompt2 != NULL) {
			pmsg[i] = &msg[i];
			msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
			msg[i++].msg = prompt2;
			++replies;
		}
		/* so call the conversation expecting i responses */
		resp = NULL;
		retval = converse(pamh, i, pmsg, &resp);

		if (resp != NULL) {

			/* interpret the response */

			if (retval == PAM_SUCCESS) {	/* a good conversation */

				token = x_strdup(resp[i - replies].resp);
				if (token != NULL) {
					if (replies == 2) {

						/* verify that password entered correctly */
						if (!resp[i - 1].resp
						    || strcmp(token, resp[i - 1].resp)) {
							_pam_delete(token);	/* mistyped */
							retval = PAM_AUTHTOK_RECOVER_ERR;
							_make_remark(pamh								    ,PAM_ERROR_MSG, MISTYPED_PASS);
						}
					}
				} else {
					_pam_log(LOG_NOTICE
						 ,"could not recover authentication token");
				}

			}
			/*
			 * tidy up the conversation (resp_retcode) is ignored
			 * -- what is it for anyway? AGM
			 */

			_pam_drop_reply(resp, i);

		} else {
			retval = (retval == PAM_SUCCESS)
			    ? PAM_AUTHTOK_RECOVER_ERR : retval;
		}
	}

	if (retval != PAM_SUCCESS) {
		if (on(WINBIND_DEBUG_ARG, ctrl))
			_pam_log(LOG_DEBUG,
			         "unable to obtain a password");
		return retval;
	}
	/* 'token' is the entered password */

	/* we store this password as an item */
	
	retval = pam_set_item(pamh, authtok_flag, token);
	_pam_delete(token);	/* clean it up */
	if (retval != PAM_SUCCESS
	    || (retval = pam_get_item(pamh, authtok_flag
				      ,(const void **) &item))
	    != PAM_SUCCESS) {
		
		_pam_log(LOG_CRIT, "error manipulating password");
		return retval;
		
	}

	*pass = item;
	item = NULL;		/* break link to password */

	return PAM_SUCCESS;
}