Add support for running a helper program to read the password when

no tty is present (or when specified with the -A flag).  TODO: docs.
This commit is contained in:
Todd C. Miller
2008-03-02 14:31:57 +00:00
parent 5b248a0765
commit ee04914164
13 changed files with 146 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1999-2005 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 1999-2005, 2008 Todd C. Miller <Todd.Miller@courtesan.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -195,6 +195,7 @@ verify_user(pw, prompt)
break; break;
} }
if (!ISSET(tgetpass_flags, TGP_ASKPASS))
pass_warn(stderr); pass_warn(stderr);
} }

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 1993-1996,1998-2005 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 1993-1996,1998-2005, 2007-2008
* Todd C. Miller <Todd.Miller@courtesan.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -98,6 +99,7 @@ check_user(validated)
status = timestamp_status(timestampdir, timestampfile, user_name, status = timestamp_status(timestampdir, timestampfile, user_name,
TS_MAKE_DIRS); TS_MAKE_DIRS);
if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) { if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
if (!ISSET(tgetpass_flags, TGP_ASKPASS))
lecture(status); lecture(status);
/* Expand any escapes in the prompt. */ /* Expand any escapes in the prompt. */

41
configure vendored
View File

@@ -1581,6 +1581,7 @@ Optional Packages:
--with-secure-path override the user's path with a built-in one --with-secure-path override the user's path with a built-in one
--without-interfaces don't try to read the ip addr of ether interfaces --without-interfaces don't try to read the ip addr of ether interfaces
--with-stow properly handle GNU stow packaging --with-stow properly handle GNU stow packaging
--with-askpass=PATH Fully qualified pathname of sudo-askpass
--with-selinux enable SELinux support --with-selinux enable SELinux support
--with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-pic try to use only PIC/non-PIC objects [default=use --with-pic try to use only PIC/non-PIC objects [default=use
@@ -3740,6 +3741,28 @@ echo "${ECHO_T}no" >&6; }
fi fi
{ echo "$as_me:$LINENO: checking path to sudo-askpass" >&5
echo $ECHO_N "checking path to sudo-askpass... $ECHO_C" >&6; }
# Check whether --with-askpass was given.
if test "${with_askpass+set}" = set; then
withval=$with_askpass; case $with_askpass in
yes) with_askpass="$libexecdir/sudo-askpass"
;;
no) ;;
*) ;;
esac
else
with_askpass="$libexecdir/sudo-askpass"
fi
{ echo "$as_me:$LINENO: result: $with_askpass" >&5
echo "${ECHO_T}$with_askpass" >&6; }
cat >>confdefs.h <<EOF
#define _PATH_SUDO_ASKPASS "$with_askpass"
EOF
{ echo "$as_me:$LINENO: checking whether to do user authentication by default" >&5 { echo "$as_me:$LINENO: checking whether to do user authentication by default" >&5
echo $ECHO_N "checking whether to do user authentication by default... $ECHO_C" >&6; } echo $ECHO_N "checking whether to do user authentication by default... $ECHO_C" >&6; }
@@ -6180,7 +6203,7 @@ ia64-*-hpux*)
;; ;;
*-*-irix6*) *-*-irix6*)
# Find out which ABI we are using. # Find out which ABI we are using.
echo '#line 6183 "configure"' > conftest.$ac_ext echo '#line 6206 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5 (eval $ac_compile) 2>&5
ac_status=$? ac_status=$?
@@ -7724,11 +7747,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7727: $lt_compile\"" >&5) (eval echo "\"\$as_me:7750: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "$as_me:7731: \$? = $ac_status" >&5 echo "$as_me:7754: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@@ -8014,11 +8037,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8017: $lt_compile\"" >&5) (eval echo "\"\$as_me:8040: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "$as_me:8021: \$? = $ac_status" >&5 echo "$as_me:8044: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@@ -8118,11 +8141,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8121: $lt_compile\"" >&5) (eval echo "\"\$as_me:8144: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "$as_me:8125: \$? = $ac_status" >&5 echo "$as_me:8148: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
@@ -10463,7 +10486,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 10466 "configure" #line 10489 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@@ -10563,7 +10586,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 10566 "configure" #line 10589 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H

View File

@@ -1010,6 +1010,17 @@ AC_ARG_WITH(stow, [ --with-stow properly handle GNU stow packaging]
;; ;;
esac], AC_MSG_RESULT(no)) esac], AC_MSG_RESULT(no))
AC_MSG_CHECKING(path to sudo-askpass)
AC_ARG_WITH(askpass, [ --with-askpass=PATH Fully qualified pathname of sudo-askpass],
[case $with_askpass in
yes) with_askpass="$libexecdir/sudo-askpass"
;;
no) ;;
*) ;;
esac], [with_askpass="$libexecdir/sudo-askpass"])
AC_MSG_RESULT($with_askpass)
SUDO_DEFINE_UNQUOTED(_PATH_SUDO_ASKPASS, "$with_askpass")
dnl dnl
dnl Options for --enable dnl Options for --enable
dnl dnl

View File

@@ -282,6 +282,10 @@ struct sudo_defs_types sudo_defs_table[] = {
"type", T_STR, "type", T_STR,
"SELinux type to use in the new security context: %s", "SELinux type to use in the new security context: %s",
NULL, NULL,
}, {
"askpass", T_STR|T_PATH,
"Path to the askpass helper program: %s",
NULL,
}, { }, {
NULL, 0, NULL NULL, 0, NULL
} }

View File

@@ -128,6 +128,8 @@
#define I_ROLE 63 #define I_ROLE 63
#define def_type (sudo_defs_table[64].sd_un.str) #define def_type (sudo_defs_table[64].sd_un.str)
#define I_TYPE 64 #define I_TYPE 64
#define def_askpass (sudo_defs_table[65].sd_un.str)
#define I_ASKPASS 65
enum def_tupple { enum def_tupple {
never, never,

View File

@@ -208,3 +208,6 @@ role
type type
T_STR T_STR
"SELinux type to use in the new security context: %s" "SELinux type to use in the new security context: %s"
askpass
T_STR|T_PATH
"Path to the askpass helper program: %s"

View File

@@ -422,6 +422,9 @@ init_defaults()
#endif #endif
#ifdef ENV_EDITOR #ifdef ENV_EDITOR
def_env_editor = TRUE; def_env_editor = TRUE;
#endif
#ifdef _PATH_SUDO_ASKPASS
def_askpass = _PATH_SUDO_ASKPASS;
#endif #endif
def_env_reset = TRUE; def_env_reset = TRUE;
def_set_logname = TRUE; def_set_logname = TRUE;

View File

@@ -84,6 +84,10 @@
#undef _PATH_SUDO_NOEXEC #undef _PATH_SUDO_NOEXEC
#endif /* _PATH_SUDO_NOEXEC */ #endif /* _PATH_SUDO_NOEXEC */
#ifndef _PATH_SUDO_ASKPASS
#undef _PATH_SUDO_ASKPASS
#endif /* _PATH_SUDO_ASKPASS */
#ifndef _PATH_VI #ifndef _PATH_VI
#undef _PATH_VI #undef _PATH_VI
#endif /* _PATH_VI */ #endif /* _PATH_VI */

20
sudo.c
View File

@@ -384,6 +384,15 @@ main(argc, argv, envp)
(void) close(fd); (void) close(fd);
} }
/* Use askpass value from sudoers unless specified by the user. */
if (def_askpass && !user_askpass)
user_askpass = def_askpass;
/* If no tty is present but DISPLAY is set, use askpass if we have it. */
if (user_askpass && !ISSET(tgetpass_flags, TGP_STDIN) &&
user_ttypath == NULL && user_display != NULL && *user_display != '\0')
SET(tgetpass_flags, TGP_ASKPASS);
/* User may have overriden environment resetting via the -E flag. */ /* User may have overriden environment resetting via the -E flag. */
if (ISSET(sudo_mode, MODE_PRESERVE_ENV) && def_setenv) if (ISSET(sudo_mode, MODE_PRESERVE_ENV) && def_setenv)
def_env_reset = FALSE; def_env_reset = FALSE;
@@ -599,7 +608,12 @@ init_vars(sudo_mode, envp)
user_tty = "unknown"; user_tty = "unknown";
for (ep = envp; *ep; ep++) { for (ep = envp; *ep; ep++) {
/* XXX - don't fill in if empty string */
switch (**ep) { switch (**ep) {
case 'D':
if (strncmp("DISPLAY=", *ep, 8) == 0)
user_display = *ep + 8;
break;
case 'K': case 'K':
if (strncmp("KRB5CCNAME=", *ep, 11) == 0) if (strncmp("KRB5CCNAME=", *ep, 11) == 0)
user_ccname = *ep + 11; user_ccname = *ep + 11;
@@ -615,8 +629,9 @@ init_vars(sudo_mode, envp)
user_prompt = *ep + 12; user_prompt = *ep + 12;
else if (strncmp("SUDO_USER=", *ep, 10) == 0) else if (strncmp("SUDO_USER=", *ep, 10) == 0)
prev_user = *ep + 10; prev_user = *ep + 10;
else if (strncmp("SUDO_ASKPASS=", *ep, 13) == 0)
user_askpass = *ep + 13;
break; break;
} }
} }
@@ -810,6 +825,9 @@ parse_args(argc, argv)
} }
switch (NewArgv[0][1]) { switch (NewArgv[0][1]) {
case 'A':
SET(tgetpass_flags, TGP_ASKPASS);
break;
case 'p': case 'p':
/* Must have an associated prompt. */ /* Must have an associated prompt. */
if (NewArgv[1] == NULL) if (NewArgv[1] == NULL)

7
sudo.h
View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1993-1996, 1998-2005, 2007 * Copyright (c) 1993-1996, 1998-2005, 2007-2008
* Todd C. Miller <Todd.Miller@courtesan.com> * Todd C. Miller <Todd.Miller@courtesan.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
@@ -54,6 +54,8 @@ struct sudo_user {
char *cmnd_safe; char *cmnd_safe;
char *class_name; char *class_name;
char *krb5_ccname; char *krb5_ccname;
char *display;
char *askpass;
int ngroups; int ngroups;
GETGROUPS_T *groups; GETGROUPS_T *groups;
struct list_member *env_vars; struct list_member *env_vars;
@@ -147,6 +149,8 @@ struct sudo_user {
#define user_host (sudo_user.host) #define user_host (sudo_user.host)
#define user_shost (sudo_user.shost) #define user_shost (sudo_user.shost)
#define user_ccname (sudo_user.krb5_ccname) #define user_ccname (sudo_user.krb5_ccname)
#define user_display (sudo_user.display)
#define user_askpass (sudo_user.askpass)
#define safe_cmnd (sudo_user.cmnd_safe) #define safe_cmnd (sudo_user.cmnd_safe)
#define login_class (sudo_user.class_name) #define login_class (sudo_user.class_name)
#define runas_pw (sudo_user._runas_pw) #define runas_pw (sudo_user._runas_pw)
@@ -174,6 +178,7 @@ struct sudo_user {
*/ */
#define TGP_ECHO 0x01 /* leave echo on when reading passwd */ #define TGP_ECHO 0x01 /* leave echo on when reading passwd */
#define TGP_STDIN 0x02 /* read from stdin, not /dev/tty */ #define TGP_STDIN 0x02 /* read from stdin, not /dev/tty */
#define TGP_ASKPASS 0x03 /* read from askpass helper program */
struct lbuf; struct lbuf;
struct passwd; struct passwd;

View File

@@ -5,9 +5,9 @@
* Usage strings for sudo. These are here because we * Usage strings for sudo. These are here because we
* need to be able to substitute values from configure. * need to be able to substitute values from configure.
*/ */
#define SUDO_USAGE1 " -h | -K | -k | -L | -V | -v" #define SUDO_USAGE1 " [-A] -h | -K | -k | -L | -V | -v"
#define SUDO_USAGE2 " -l[l] [-g groupname|#gid] [-U username] [-u username|#uid] [-g groupname|#gid] [command]" #define SUDO_USAGE2 " -l[l] [-A] [-g groupname|#gid] [-U username] [-u username|#uid] [-g groupname|#gid] [command]"
#define SUDO_USAGE3 " [-bEHPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C fd] @LOGINCAP_USAGE@[-g groupname|#gid] [-p prompt] [-u username|#uid] [-g groupname|#gid] [VAR=value] [-i|-s] [<command>]" #define SUDO_USAGE3 " [-AbEHPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C fd] @LOGINCAP_USAGE@[-g groupname|#gid] [-p prompt] [-u username|#uid] [-g groupname|#gid] [VAR=value] [-i|-s] [<command>]"
#define SUDO_USAGE4 " -e [-S] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C fd] @LOGINCAP_USAGE@[-g groupname|#gid] [-p prompt] [-u username|#uid] file ..." #define SUDO_USAGE4 " -e [-AS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C fd] @LOGINCAP_USAGE@[-g groupname|#gid] [-p prompt] [-u username|#uid] file ..."
#endif /* _SUDO_USAGE_H */ #endif /* _SUDO_USAGE_H */

View File

@@ -1,5 +1,6 @@
/* /*
* Copyright (c) 1996, 1998-2005 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 1996, 1998-2005, 2007-2008
* Todd C. Miller <Todd.Miller@courtesan.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -122,6 +123,7 @@ static volatile sig_atomic_t signo;
static void handler __P((int)); static void handler __P((int));
static char *getln __P((int, char *, size_t)); static char *getln __P((int, char *, size_t));
static char *sudo_askpass(const char *);
/* /*
* Like getpass(3) but with timeout and echo flags. * Like getpass(3) but with timeout and echo flags.
@@ -140,6 +142,11 @@ tgetpass(prompt, timeout, flags)
int input, output, save_errno; int input, output, save_errno;
(void) fflush(stdout); (void) fflush(stdout);
/* If using a helper program to get the password, run it instead. */
if (ISSET(flags, TGP_ASKPASS) && user_askpass)
return(sudo_askpass(prompt));
restart: restart:
signo = 0; signo = 0;
pass = NULL; pass = NULL;
@@ -232,6 +239,49 @@ restart:
return(pass); return(pass);
} }
/*
* Fork a child and exec sudo-askpass to get the password from the user.
*/
static char *
sudo_askpass(prompt)
const char *prompt;
{
static char buf[SUDO_PASS_MAX + 1], *pass;
sigaction_t sa, saved_sa_pipe;
int pfd[2];
pid_t pid;
if (pipe(pfd) == -1)
error(1, "unable to create pipe");
if ((pid = fork()) == -1)
error(1, "unable to fork");
if (pid == 0) {
/* child, point stdout to output side of the pipe and exec askpass */
(void) dup2(pfd[1], STDOUT_FILENO);
set_perms(PERM_FULL_USER);
closefrom(STDERR_FILENO + 1);
execl(user_askpass, user_askpass, prompt, (char *)NULL);
warning("unable to run %s", user_askpass);
_exit(255);
}
/* Ignore SIGPIPE in case child exits prematurely */
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
(void) sigaction(SIGPIPE, &sa, &saved_sa_pipe);
/* Get response from child (askpass) and restore SIGPIPE handler */
(void) close(pfd[1]);
pass = getln(pfd[0], buf, sizeof(buf));
(void) close(pfd[0]);
(void) sigaction(SIGPIPE, &saved_sa_pipe, NULL);
return(pass);
}
static char * static char *
getln(fd, buf, bufsiz) getln(fd, buf, bufsiz)
int fd; int fd;