From: David Sommerseth <dav...@redhat.com> This is will provide an interface for other mechanisms to be used to query the user for information, such as usernames, passwords, etc.
It has also been a goal to make it possible to query for all the information in one call and not do it sequencially as before. Signed-off-by: David Sommerseth <dav...@redhat.com> --- src/openvpn/Makefile.am | 2 +- src/openvpn/console.c | 228 ++++++--------------------------------- src/openvpn/console.h | 92 +++++++++++++++- src/openvpn/console_builtin.c | 246 ++++++++++++++++++++++++++++++++++++++++++ src/openvpn/misc.c | 79 +++++++++++--- src/openvpn/pkcs11.c | 14 ++- 6 files changed, 449 insertions(+), 212 deletions(-) create mode 100644 src/openvpn/console_builtin.c diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index d089f50..e07389f 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -69,7 +69,7 @@ openvpn_SOURCES = \ memdbg.h \ misc.c misc.h \ platform.c platform.h \ - console.c console.h \ + console.c console.h console_builtin.c \ mroute.c mroute.h \ mss.c mss.h \ mstats.c mstats.h \ diff --git a/src/openvpn/console.c b/src/openvpn/console.c index d66d408..3198cd0 100644 --- a/src/openvpn/console.c +++ b/src/openvpn/console.c @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2014 David Sommerseth <dav...@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -30,213 +31,54 @@ #include "syshead.h" #include "console.h" -#include "error.h" #include "buffer.h" -#include "misc.h" -#ifdef ENABLE_SYSTEMD -#include <systemd/sd-daemon.h> -#endif - -#ifdef WIN32 - -#include "win32.h" - -/* - * Get input from console. - * - * Return false on input error, or if service - * exit event is signaled. - */ -static bool -get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) +bool query_user_init(struct gc_arena *gc, struct query_user ** setup) { - HANDLE in = INVALID_HANDLE_VALUE; - HANDLE err = INVALID_HANDLE_VALUE; - DWORD len = 0; + ASSERT (&gc != NULL); - ASSERT (prompt); - ASSERT (input); - ASSERT (capacity > 0); - - input[0] = '\0'; - - in = GetStdHandle (STD_INPUT_HANDLE); - err = get_orig_stderr (); - - if (in != INVALID_HANDLE_VALUE - && err != INVALID_HANDLE_VALUE - && !win32_service_interrupt (&win32_signal) - && WriteFile (err, prompt, strlen (prompt), &len, NULL)) + *setup = (struct query_user *) gc_malloc (sizeof(**setup)+2, true, gc); + if( *setup == NULL ) { - bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); - DWORD flags_save = 0; - int status = 0; - WCHAR *winput; - - if (is_console) - { - if (GetConsoleMode (in, &flags_save)) - { - DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; - if (echo) - flags |= ENABLE_ECHO_INPUT; - SetConsoleMode (in, flags); - } - else - is_console = 0; - } - - if (is_console) - { - winput = malloc (capacity * sizeof (WCHAR)); - if (winput == NULL) - return false; - - status = ReadConsoleW (in, winput, capacity, &len, NULL); - WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); - free (winput); - } - else - status = ReadFile (in, input, capacity, &len, NULL); - - string_null_terminate (input, (int)len, capacity); - chomp (input); - - if (!echo) - WriteFile (err, "\r\n", 2, &len, NULL); - if (is_console) - SetConsoleMode (in, flags_save); - if (status && !win32_service_interrupt (&win32_signal)) - return true; + return false; } - - return false; + (*setup)->_gc = gc; + (*setup)->next = NULL; /* Pure fail safety */ + return true; } -#endif - -#ifdef HAVE_GETPASS - -static FILE * -open_tty (const bool write) -{ - FILE *ret; - ret = fopen ("/dev/tty", write ? "w" : "r"); - if (!ret) - ret = write ? stderr : stdin; - return ret; -} -static void -close_tty (FILE *fp) +bool query_user_add(struct query_user * setup, + char *prompt, size_t prompt_len, + char *resp, size_t resp_len, + bool query, bool echo) { - if (fp != stderr && fp != stdin) - fclose (fp); -} - -#endif + struct query_user *p = NULL; -#ifdef ENABLE_SYSTEMD + ASSERT (setup != NULL); -/* - * is systemd running - */ - -static bool -check_systemd_running () -{ - struct stat c; - - /* We simply test whether the systemd cgroup hierarchy is - * mounted, as well as the systemd-ask-password executable - * being available */ - - return (sd_booted() > 0) - && (stat(SYSTEMD_ASK_PASSWORD_PATH, &c) == 0); - -} - -static bool -get_console_input_systemd (const char *prompt, const bool echo, char *input, const int capacity) -{ - int std_out; - bool ret = false; - struct argv argv; - - argv_init (&argv); - argv_printf (&argv, SYSTEMD_ASK_PASSWORD_PATH); - argv_printf_cat (&argv, "%s", prompt); - - if ((std_out = openvpn_popen (&argv, NULL)) < 0) { - return false; - } - CLEAR (*input); - if (read (std_out, input, capacity) != 0) + /* Ensure prompt_len is set and we have info about the response buffer + * if asked to query the user for user input + */ + if( prompt_len == 0 || (query == true && (resp_len == 0 || resp == NULL)) ) { - chomp (input); - ret = true; + return false; } - close (std_out); - - argv_reset (&argv); - - return ret; -} - - -#endif -/* - * Get input from console - */ -bool -get_console_input (const char *prompt, const bool echo, char *input, const int capacity) -{ - bool ret = false; - ASSERT (prompt); - ASSERT (input); - ASSERT (capacity > 0); - input[0] = '\0'; - -#ifdef ENABLE_SYSTEMD - if (check_systemd_running ()) - return get_console_input_systemd (prompt, echo, input, capacity); -#endif - -#if defined(WIN32) - return get_console_input_win32 (prompt, echo, input, capacity); -#elif defined(HAVE_GETPASS) - if (echo) - { - FILE *fp; - - fp = open_tty (true); - fprintf (fp, "%s", prompt); - fflush (fp); - close_tty (fp); - - fp = open_tty (false); - if (fgets (input, capacity, fp) != NULL) - { - chomp (input); - ret = true; - } - close_tty (fp); - } - else - { - char *gp = getpass (prompt); - if (gp) - { - strncpynt (input, gp, capacity); - memset (gp, 0, strlen (gp)); - ret = true; - } - } -#else - msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); -#endif - return ret; + /* Seek to the end of the pointer chain */ + QUERY_USER_FOREACH(setup, p); + + /* Save the information needed for the user interaction */ + p->_gc = setup->_gc; + p->prompt = prompt; + p->prompt_len = prompt_len; + p->response = resp; + p->response_len = resp_len; + p->query = query; + p->echo = echo; + query_user_init(p->_gc, &p->next); + //p->next = (struct query_user *) gc_malloc (sizeof(*setup)+2, true, p->_gc); + + return (p->next != NULL) ? true : false; } diff --git a/src/openvpn/console.h b/src/openvpn/console.h index 268f3fe..1500ae2 100644 --- a/src/openvpn/console.h +++ b/src/openvpn/console.h @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2014 David Sommerseth <dav...@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -26,8 +27,95 @@ #define CONSOLE_H #include "basic.h" +#include "buffer.h" -bool -get_console_input (const char *prompt, const bool echo, char *input, const int capacity); +/** + * Configuration setup for declaring what kind of information to ask a user for + */ +struct query_user { + struct gc_arena *_gc; /**< Local pointer to the gc_arena maintaining this chained list */ + char *prompt; /**< Prompt to present to the user */ + size_t prompt_len; /**< Lenght of the prompt string */ + char *response; /**< The user's response */ + size_t response_len; /**< Lenght the of the user reposone */ + bool query; /**< True: Expect user to provide a response, False: only print */ + bool echo; /**< True: The user should see what is being typed, otherwise mask it */ + struct query_user *next; /**< Next information to query for, NULL indicates end of list */ +}; + + +/** + * Initializes the framework for querying for user input + * + * @param gc Reference to an gc_arena object, taking care of memory management + * @param setup Returns the initialized object for user input information + * + * @return True if initialization was successfull, otherwise False. + */ +bool query_user_init (struct gc_arena *gc, struct query_user ** setup); + + +/** + * Adds an item to ask the user for + * + * @param setup Pointer to the setup defining what to ask the user + * @param prompt Prompt to display to the user + * @param prompt_len Length of the prompt string + * @param resp String containing the user response + * @param resp_len Lenght of the response string + * @param query Is a user response expected? If False, only present information to user + * @param echo Should the user input be echoed to the user? If False, input will be masked + * + * @return True if adding this information to the setup was successful, otherwise False + * + */ +bool query_user_add (struct query_user * setup, + char *prompt, size_t prompt_len, + char *resp, size_t resp_len, + bool query, bool echo); + + +/** + * Executes a configured setup, using the built-in method for querying the user. + * This method uses the console/TTY directly. + * + * @param setup Pointer to the setup defining what to ask the user + * + * @return True if executing all the defined steps completed successfully + */ +bool query_user_exec_builtin (struct query_user * setup); + + +#ifdef QUERY_USER_EXEC_ALTERNATIVE +/** + * Executes a configured setup, using the compiled method for querying the user + * + * @param setup Pointer to the setup defining what to ask the user + * + * @return True if executing all the defined steps completed successfully + */ +bool query_user_exec (struct query_user * setup); + +#else /* QUERY_USER_EXEC_ALTERNATIVE not defined*/ +/** + * Wrapper function enabling query_user_exec() if no alternative methods have + * been enabled + * + */ +static bool query_user_exec (struct query_user * setup) +{ + return query_user_exec_builtin(setup); +} +#endif /* QUERY_USER_EXEC_DECLARED */ + + +/** + * Simple for-loop helper for traversing the query setup + * + * @param setup_start Starting point to start the traversing + * @param element Current element to be processed + * + */ +#define QUERY_USER_FOREACH(setup_start, element) for (element=setup_start; element->next != NULL; element=element->next) #endif diff --git a/src/openvpn/console_builtin.c b/src/openvpn/console_builtin.c new file mode 100644 index 0000000..44e2a8f --- /dev/null +++ b/src/openvpn/console_builtin.c @@ -0,0 +1,246 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2014 David Sommerseth <dav...@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * These functions covers handing user input/output using the default consoles + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "console.h" +#include "error.h" +#include "buffer.h" +#include "misc.h" + +#ifdef WIN32 + +#include "win32.h" + +/** + * Get input from a Windows console. + * + * @param prompt Prompt to display to the user + * @param echo Should the user input be displayed in the console + * @param input Pointer to the buffer the user input will be saved + * @param capacity Size of the buffer for the user input + * + * @return Return false on input error, or if service + * exit event is signaled. + */ +static bool _get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) +{ + HANDLE in = INVALID_HANDLE_VALUE; + HANDLE err = INVALID_HANDLE_VALUE; + DWORD len = 0; + + ASSERT (prompt); + ASSERT (input); + ASSERT (capacity > 0); + + input[0] = '\0'; + + in = GetStdHandle (STD_INPUT_HANDLE); + err = get_orig_stderr (); + + if (in != INVALID_HANDLE_VALUE + && err != INVALID_HANDLE_VALUE + && !win32_service_interrupt (&win32_signal) + && WriteFile (err, prompt, strlen (prompt), &len, NULL)) + { + bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); + DWORD flags_save = 0; + int status = 0; + WCHAR *winput; + + if (is_console) + { + if (GetConsoleMode (in, &flags_save)) + { + DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; + if (echo) + flags |= ENABLE_ECHO_INPUT; + SetConsoleMode (in, flags); + } else + is_console = 0; + } + + if (is_console) + { + winput = malloc (capacity * sizeof (WCHAR)); + if (winput == NULL) + return false; + + status = ReadConsoleW (in, winput, capacity, &len, NULL); + WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); + free (winput); + } else + status = ReadFile (in, input, capacity, &len, NULL); + + string_null_terminate (input, (int)len, capacity); + chomp (input); + + if (!echo) + WriteFile (err, "\r\n", 2, &len, NULL); + if (is_console) + SetConsoleMode (in, flags_save); + if (status && !win32_service_interrupt (&win32_signal)) + return true; + } + + return false; +} + +#endif /* WIN32 */ + + +#ifdef HAVE_GETPASS + +/** + * Open the current console TTY for read/write operations + * + * @params write If true, the user wants to write to the console + * otherwise read from the console + * + * @returns Returns a FILE pointer to either the TTY in read or write mode + * or stdin/stderr, depending on the write flag + * + */ +static FILE * open_tty (const bool write) +{ + FILE *ret; + ret = fopen ("/dev/tty", write ? "w" : "r"); + if (!ret) + ret = write ? stderr : stdin; + return ret; +} + +/** + * Closes the TTY FILE pointer, but only if it is not a stdin/stderr FILE object. + * + * @params fp FILE pointer to close + * + */ +static void close_tty (FILE *fp) +{ + if (fp != stderr && fp != stdin) + fclose (fp); +} + +#endif /* HAVE_GETPASS */ + + +/** + * Core function for getting input from console + * + * @params prompt The prompt to present to the user + * @params echo Should the user see what is being typed + * @params input Pointer to the buffer used to save the user input + * @params capacity Size of the input buffer + * + * @returns Returns True if user input was gathered + */ +static bool _get_console_input (const char *prompt, const bool echo, char *input, const int capacity) +{ + bool ret = false; + ASSERT (prompt); + ASSERT (input); + ASSERT (capacity > 0); + input[0] = '\0'; + +#if defined(WIN32) + return _get_console_input_win32 (prompt, echo, input, capacity); +#elif defined(HAVE_GETPASS) + if (echo) + { + FILE *fp; + + fp = open_tty (true); + fprintf (fp, "%s", prompt); + fflush (fp); + close_tty (fp); + + fp = open_tty (false); + if (fgets (input, capacity, fp) != NULL) + { + chomp (input); + ret = true; + } + close_tty (fp); + } else { + char *gp = getpass (prompt); + if (gp) + { + strncpynt (input, gp, capacity); + memset (gp, 0, strlen (gp)); + ret = true; + } + } +#else + msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); +#endif + return ret; +} + + +/** + * @copydoc query_user_exec() + * + * Default method for querying user using default stdin/stdout on a console. + * This needs to be available as a backup interface for the alternative + * implementations in case they cannot query through their implementation + * specific methods. + * + * If no alternative implementation is declared, a wrapper in console.h will ensure + * query_user_exec() will call this function instead. + * + */ +bool query_user_exec_builtin(struct query_user * setup) +{ + struct query_user *p; + bool ret = true; + + ASSERT( setup != NULL ); + + /* Loop through the complete query setup and when needed, collect the information */ + QUERY_USER_FOREACH(setup, p) + { + if( p->query ) + { + /* If the user is expected to provide some data */ + ret &= _get_console_input(p->prompt, p->echo, p->response, p->response_len); + } else { + /* If the prompt should only be presented to the user with no further action */ + msg(M_INFO|M_NOPREFIX, "%s", p->prompt); + } + } + + return ret; +} diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c index 61bc523..9caed89 100644 --- a/src/openvpn/misc.c +++ b/src/openvpn/misc.c @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2014 David Sommerseth <dav...@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -1075,11 +1076,20 @@ get_user_pass_cr (struct user_pass *up, */ if (flags & GET_USER_PASS_NEED_OK) { + struct query_user *qucfg = NULL; struct buffer user_prompt = alloc_buf_gc (128, &gc); + if( !query_user_init (&gc, &qucfg) ) + { + msg (M_FATAL, + "ERROR: Could not prepare for retrieving user input for %s ok-confirmation", + prefix); + } buf_printf (&user_prompt, "NEED-OK|%s|%s:", prefix, up->username); + query_user_add (qucfg, BSTR(&user_prompt), BLEN(&user_prompt), up->password, USER_PASS_LEN, + true, true); - if (!get_console_input (BSTR (&user_prompt), true, up->password, USER_PASS_LEN)) + if (!query_user_exec (qucfg)) msg (M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix); if (!strlen (up->password)) @@ -1097,13 +1107,26 @@ get_user_pass_cr (struct user_pass *up, struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc); if (ac) { + struct query_user *qucfg = NULL; char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); - struct buffer packed_resp; + struct buffer packed_resp, challenge; + if( !query_user_init (&gc, &qucfg) ) + { + msg (M_FATAL, "ERROR: Could not prepare for challenge/response dialogue"); + } + + challenge = alloc_buf_gc (14+strlen(ac->challenge_text), &gc); + buf_printf (&challenge, "CHALLENGE: %s", ac->challenge_text); buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN); - msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", ac->challenge_text); - if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read challenge response from stdin"); + + query_user_add (qucfg, BSTR(&challenge), BLEN(&challenge), response, USER_PASS_LEN, + true, BOOL_CAST(ac->flags&CR_ECHO)); + + if (!query_user_exec (qucfg)) + { + msg (M_FATAL, "ERROR: could not read challenge response from stdin"); + } strncpynt (up->username, ac->user, USER_PASS_LEN); buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response); } @@ -1115,33 +1138,61 @@ get_user_pass_cr (struct user_pass *up, else #endif { + struct query_user *qucfg = NULL; struct buffer user_prompt = alloc_buf_gc (128, &gc); struct buffer pass_prompt = alloc_buf_gc (128, &gc); + if( !query_user_init (&gc, &qucfg) ) + { + msg (M_FATAL, "ERROR: Could not prepare for username/password dialogue"); + } + buf_printf (&user_prompt, "Enter %s Username:", prefix); buf_printf (&pass_prompt, "Enter %s Password:", prefix); if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) { - if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); + query_user_add (qucfg, BSTR(&user_prompt), BLEN(&user_prompt), + up->username, USER_PASS_LEN, true, true); + } + + query_user_add (qucfg, BSTR(&pass_prompt), BLEN(&pass_prompt), + up->password, USER_PASS_LEN, true, false); + + if( !query_user_exec (qucfg) ) + { + msg( M_FATAL, "ERROR: Failed retrieving username or password"); + } + + if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) + { if (strlen (up->username) == 0) msg (M_FATAL, "ERROR: %s username is empty", prefix); } - if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); - #ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE)) { + struct query_user *qucfg = NULL; char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); - struct buffer packed_resp; + struct buffer packed_resp, challenge; char *pw64=NULL, *resp64=NULL; - msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", auth_challenge); - if (!get_console_input ("Response:", BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO), response, USER_PASS_LEN)) - msg (M_FATAL, "ERROR: could not read static challenge response from stdin"); + if( !query_user_init (&gc, &qucfg) ) + { + msg (M_FATAL, "ERROR: Could not prepare for username/password dialogue"); + } + + challenge = alloc_buf_gc (14+strlen(auth_challenge), &gc); + buf_printf (&challenge, "CHALLENGE: %s", auth_challenge); + + query_user_add (qucfg, BSTR(&challenge), BLEN(&challenge), response, USER_PASS_LEN, + true, BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO)); + + if (!query_user_exec (qucfg)) + { + msg (M_FATAL, "ERROR: could not retrieve static challenge response"); + } if (openvpn_base64_encode(up->password, strlen(up->password), &pw64) == -1 || openvpn_base64_encode(response, strlen(response), &resp64) == -1) msg (M_FATAL, "ERROR: could not base64-encode password/static_response"); diff --git a/src/openvpn/pkcs11.c b/src/openvpn/pkcs11.c index 3a15ef6..75ac392 100644 --- a/src/openvpn/pkcs11.c +++ b/src/openvpn/pkcs11.c @@ -6,6 +6,7 @@ * packet compression. * * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * Copyright (C) 2014 David Sommerseth <dav...@redhat.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -740,6 +741,7 @@ _pkcs11_openvpn_show_pkcs11_ids_pin_prompt ( const size_t pin_max ) { struct gc_arena gc = gc_new (); + struct query_user *qucfg = NULL; struct buffer pass_prompt = alloc_buf_gc (128, &gc); (void)global_data; @@ -748,10 +750,18 @@ _pkcs11_openvpn_show_pkcs11_ids_pin_prompt ( ASSERT (token!=NULL); + if( !query_user_init (&gc, &qucfg) ) + { + msg (M_FATAL, "ERROR: Could not prepare for token PIN dialogue"); + } + buf_printf (&pass_prompt, "Please enter '%s' token PIN or 'cancel': ", token->display); + query_user_add(qucfg, BSTR(&pass_prompt), BLEN(&pass_prompt), + pin, pin_max, true, false); - if (!get_console_input (BSTR (&pass_prompt), false, pin, pin_max)) { - msg (M_FATAL, "Cannot read password from stdin"); + if (!query_user_exec (qucfg)) + { + msg (M_FATAL, "Could not retrieve the PIN"); } gc_free (&gc); -- 1.8.3.1