Add a new API function, openpam_subst(3), which replaces substitution

codes in a string with the values of selected PAM items.  Use it for
prompts.

Furthermore, modify pam_get_user(3) and pam_get_authtok(3) to look for
module options named {user,authtok,oldauthtok}_prompt, as appropriate.
If found, these options take precedence over both the caller's prompt
and the PAM_{USER,AUTHTOK,OLDAUTHTOK}_PROMPT items.  The usefulness of
these options is somewhat limited by the fact that the policy file
parser does not support quoted strings; that's next on the todo list.


git-svn-id: svn+ssh://svn.openpam.org/svn/openpam/trunk@455 185d5e19-27fe-0310-9dcf-9bff6b9f3609
This commit is contained in:
Dag-Erling Smørgrav 2011-10-29 18:31:11 +00:00
parent 8b88ff5959
commit 81b5c45be2
7 changed files with 238 additions and 14 deletions

10
HISTORY
View File

@ -2,6 +2,16 @@ OpenPAM Lycopsida 2011-??-??
- ENHANCE: removed static build autodetection, which didn't work anyway.
Use an explicit, user-specified preprocessor variable instead.
- ENHANCE: cleaned up the documentation a bit.
- ENHANCE: added openpam_subst(3), allowing certain PAM items to be
embedded in strings such as prompts. Apply it to the prompts used
by pam_get_user(3) and pam_get_authtok(3).
- ENHANCE: add support for the user_prompt, authtok_prompt and
oldauthtok_prompt module options, which override the prompts passed
by the module to pam_set_user(3) and pam_get_authtok(3).
============================================================================
OpenPAM Hydrangea 2007-12-21

View File

@ -44,6 +44,7 @@ OMAN = \
openpam_readline.3 \
openpam_restore_cred.3 \
openpam_set_option.3 \
openpam_subst.3 \
openpam_ttyconv.3 \
pam_error.3 \
pam_get_authtok.3 \

View File

@ -59,6 +59,12 @@ openpam_borrow_cred(pam_handle_t *_pamh,
const struct passwd *_pwd)
OPENPAM_NONNULL((1,2));
int
openpam_subst(const pam_handle_t *_pamh,
char *_buf,
size_t *_bufsize,
const char *_template);
void
openpam_free_data(pam_handle_t *_pamh,
void *_data,

View File

@ -24,6 +24,7 @@ libpam_la_SOURCES = \
openpam_restore_cred.c \
openpam_set_option.c \
openpam_static.c \
openpam_subst.c \
openpam_ttyconv.c \
pam_acct_mgmt.c \
pam_authenticate.c \

168
lib/openpam_subst.c Normal file
View File

@ -0,0 +1,168 @@
/*-
* Copyright (c) 2011 Dag-Erling Smørgrav
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <security/pam_appl.h>
#include "openpam_impl.h"
#define subst_char(ch) do { \
int _ch = (ch); \
if (buf && len < *bufsize) \
*buf++ = _ch; \
++len; \
} while (0)
#define subst_string(s) do { \
const char *_s = (s); \
while (*_s) \
subst_char(*_s++); \
} while (0)
#define subst_item(i) do { \
int _i = (i); \
const void *_p; \
ret = pam_get_item(pamh, _i, &_p); \
if (ret == PAM_SUCCESS && _p != NULL) \
subst_string(_p); \
} while (0)
/*
* OpenPAM internal
*
* Substitute PAM item values in a string
*/
int
openpam_subst(const pam_handle_t *pamh,
char *buf, size_t *bufsize, const char *template)
{
size_t len;
int ret;
ENTERS(template);
if (template == NULL)
template = "(null)";
len = 1; /* initialize to 1 for terminating NUL */
ret = PAM_SUCCESS;
while (*template && ret == PAM_SUCCESS) {
if (template[0] == '%') {
++template;
switch (*template) {
case 's':
subst_item(PAM_SERVICE);
break;
case 't':
subst_item(PAM_TTY);
break;
case 'h':
subst_item(PAM_HOST);
break;
case 'u':
subst_item(PAM_USER);
break;
case 'H':
subst_item(PAM_RHOST);
break;
case 'U':
subst_item(PAM_RUSER);
break;
case '\0':
subst_char('%');
break;
default:
subst_char('%');
subst_char(*template);
}
++template;
} else {
subst_char(*template++);
}
}
if (buf)
*buf = '\0';
if (ret == PAM_SUCCESS) {
if (len > *bufsize)
ret = PAM_TRY_AGAIN;
*bufsize = len;
}
RETURNC(ret);
}
/*
* Error codes:
*
* =pam_get_item
* !PAM_SYMBOL_ERR
* PAM_TRY_AGAIN
*/
/**
* The =openpam_subst function expands a string, substituting PAM item
* values for all occurrences of specific substitution codes.
* The =template argument points to the initial string.
* The result is stored in the buffer pointed to by the =buf argument; the
* =bufsize argument specifies the size of that buffer.
* The actual size of the resulting string, including the terminating NUL
* character, is stored in the location pointed to by the =bufsize
* argument.
*
* If =buf is NULL, or if the buffer is too small to hold the expanded
* string, =bufsize is updated to reflect the amount of space required to
* hold the entire string, and =openpam_subst returns =PAM_TRY_AGAIN.
*
* If =openpam_subst fails for any other reason, the =bufsize argument is
* untouched, but part of the buffer may still have been overwritten.
*
* Substitution codes are introduced by a percent character and correspond
* to PAM items:
*
* %H:
* Replaced by the current value of the =PAM_RHOST item.
* %h:
* Replaced by the current value of the =PAM_HOST item.
* %s:
* Replaced by the current value of the =PAM_SERVICE item.
* %t:
* Replaced by the current value of the =PAM_TTY item.
* %U:
* Replaced by the current value of the =PAM_RUSER item.
* %u:
* Replaced by the current value of the =PAM_USER item.
*
* >pam_get_authtok
* >pam_get_item
* >pam_get_user
*
* AUTHOR DES
*/

View File

@ -65,8 +65,10 @@ pam_get_authtok(pam_handle_t *pamh,
const char **authtok,
const char *prompt)
{
char prompt_buf[1024];
size_t prompt_size;
const void *oldauthtok, *prevauthtok, *promptp;
const char *default_prompt;
const char *prompt_option, *default_prompt;
char *resp, *resp2;
int pitem, r, style, twice;
@ -78,6 +80,7 @@ pam_get_authtok(pam_handle_t *pamh,
switch (item) {
case PAM_AUTHTOK:
pitem = PAM_AUTHTOK_PROMPT;
prompt_option = "authtok_prompt";
default_prompt = authtok_prompt;
r = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldauthtok);
if (r == PAM_SUCCESS && oldauthtok != NULL) {
@ -87,6 +90,7 @@ pam_get_authtok(pam_handle_t *pamh,
break;
case PAM_OLDAUTHTOK:
pitem = PAM_OLDAUTHTOK_PROMPT;
prompt_option = "oldauthtok_prompt";
default_prompt = oldauthtok_prompt;
twice = 0;
break;
@ -103,13 +107,21 @@ pam_get_authtok(pam_handle_t *pamh,
else if (openpam_get_option(pamh, "use_first_pass"))
RETURNC(r == PAM_SUCCESS ? PAM_AUTH_ERR : r);
}
if (prompt == NULL) {
r = pam_get_item(pamh, pitem, &promptp);
if (r != PAM_SUCCESS || promptp == NULL)
prompt = default_prompt;
else
/* pam policy overrides the module's choice */
if ((promptp = openpam_get_option(pamh, prompt_option)) != NULL)
prompt = promptp;
/* no prompt provided, see if there is one tucked away somewhere */
if (prompt == NULL)
if (pam_get_item(pamh, pitem, &promptp) && promptp != NULL)
prompt = promptp;
}
/* fall back to hardcoded default */
if (prompt == NULL)
prompt = default_prompt;
/* expand */
prompt_size = sizeof prompt_buf;
r = openpam_subst(pamh, prompt_buf, &prompt_size, prompt);
if (r == PAM_SUCCESS && prompt_size <= sizeof prompt_buf)
prompt = prompt_buf;
style = openpam_get_option(pamh, "echo_pass") ?
PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF;
r = pam_prompt(pamh, style, &resp, "%s", prompt);
@ -164,6 +176,13 @@ pam_get_authtok(pam_handle_t *pamh,
* If it is =NULL, the =PAM_AUTHTOK_PROMPT or =PAM_OLDAUTHTOK_PROMPT item,
* as appropriate, will be used.
* If that item is also =NULL, a hardcoded default prompt will be used.
* Either way, the prompt is expanded using =openpam_subst before it is
* passed to the conversation function.
*
* If =pam_get_authtok is called from a module and the ;authtok_prompt /
* ;oldauthtok_prompt option is set in the policy file, the value of that
* option takes precedence over both the =prompt argument and the
* =PAM_AUTHTOK_PROMPT / =PAM_OLDAUTHTOK_PROMPT item.
*
* If =item is set to =PAM_AUTHTOK and there is a non-null =PAM_OLDAUTHTOK
* item, =pam_get_authtok will ask the user to confirm the new token by
@ -172,4 +191,5 @@ pam_get_authtok(pam_handle_t *pamh,
*
* >pam_get_item
* >pam_get_user
* >openpam_subst
*/

View File

@ -62,6 +62,8 @@ pam_get_user(pam_handle_t *pamh,
const char **user,
const char *prompt)
{
char prompt_buf[1024];
size_t prompt_size;
const void *promptp;
char *resp;
int r;
@ -72,13 +74,22 @@ pam_get_user(pam_handle_t *pamh,
r = pam_get_item(pamh, PAM_USER, (const void **)user);
if (r == PAM_SUCCESS && *user != NULL)
RETURNC(PAM_SUCCESS);
if (prompt == NULL) {
r = pam_get_item(pamh, PAM_USER_PROMPT, &promptp);
if (r != PAM_SUCCESS || promptp == NULL)
prompt = user_prompt;
else
/* pam policy overrides the module's choice */
if ((promptp = openpam_get_option(pamh, "user_prompt")) != NULL)
prompt = promptp;
/* no prompt provided, see if there is one tucked away somewhere */
if (prompt == NULL)
if (pam_get_item(pamh, PAM_USER_PROMPT, &promptp) &&
promptp != NULL)
prompt = promptp;
}
/* fall back to hardcoded default */
if (prompt == NULL)
prompt = user_prompt;
/* expand */
prompt_size = sizeof prompt_buf;
r = openpam_subst(pamh, prompt_buf, &prompt_size, prompt);
if (r == PAM_SUCCESS && prompt_size <= sizeof prompt_buf)
prompt = prompt_buf;
r = pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &resp, "%s", prompt);
if (r != PAM_SUCCESS)
RETURNC(r);
@ -109,9 +120,16 @@ pam_get_user(pam_handle_t *pamh,
*
* The =prompt argument specifies a prompt to use if no user name is
* cached.
* If it is =NULL, the =PAM_USER_PROMPT will be used.
* If it is =NULL, the =PAM_USER_PROMPT item will be used.
* If that item is also =NULL, a hardcoded default prompt will be used.
* Either way, the prompt is expanded using =openpam_subst before it is
* passed to the conversation function.
*
* If =pam_get_user is called from a module and the ;user_prompt option is
* set in the policy file, the value of that option takes precedence over
* both the =prompt argument and the =PAM_USER_PROMPT item.
*
* >pam_get_item
* >pam_get_authtok
* >openpam_subst
*/