1/* 2 * OpenVPN -- An application to securely tunnel IP networks 3 * over a single UDP port, with support for SSL/TLS-based 4 * session authentication and key exchange, 5 * packet encryption, packet authentication, and 6 * packet compression. 7 * 8 * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 12 * as published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program (see the file COPYING included with this 21 * distribution); if not, write to the Free Software Foundation, Inc., 22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25#ifdef HAVE_CONFIG_H 26#include "config.h" 27#elif defined(_MSC_VER) 28#include "config-msvc.h" 29#endif 30 31#include "syshead.h" 32#include "console.h" 33#include "error.h" 34#include "buffer.h" 35#include "misc.h" 36 37#ifdef WIN32 38 39#include "win32.h" 40 41/* 42 * Get input from console. 43 * 44 * Return false on input error, or if service 45 * exit event is signaled. 46 */ 47 48static bool 49get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity) 50{ 51 HANDLE in = INVALID_HANDLE_VALUE; 52 HANDLE err = INVALID_HANDLE_VALUE; 53 DWORD len = 0; 54 55 ASSERT (prompt); 56 ASSERT (input); 57 ASSERT (capacity > 0); 58 59 input[0] = '\0'; 60 61 in = GetStdHandle (STD_INPUT_HANDLE); 62 err = get_orig_stderr (); 63 64 if (in != INVALID_HANDLE_VALUE 65 && err != INVALID_HANDLE_VALUE 66 && !win32_service_interrupt (&win32_signal) 67 && WriteFile (err, prompt, strlen (prompt), &len, NULL)) 68 { 69 bool is_console = (GetFileType (in) == FILE_TYPE_CHAR); 70 DWORD flags_save = 0; 71 int status = 0; 72 WCHAR *winput; 73 74 if (is_console) 75 { 76 if (GetConsoleMode (in, &flags_save)) 77 { 78 DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; 79 if (echo) 80 flags |= ENABLE_ECHO_INPUT; 81 SetConsoleMode (in, flags); 82 } 83 else 84 is_console = 0; 85 } 86 87 if (is_console) 88 { 89 winput = malloc (capacity * sizeof (WCHAR)); 90 if (winput == NULL) 91 return false; 92 93 status = ReadConsoleW (in, winput, capacity, &len, NULL); 94 WideCharToMultiByte (CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); 95 free (winput); 96 } 97 else 98 status = ReadFile (in, input, capacity, &len, NULL); 99 100 string_null_terminate (input, (int)len, capacity); 101 chomp (input); 102 103 if (!echo) 104 WriteFile (err, "\r\n", 2, &len, NULL); 105 if (is_console) 106 SetConsoleMode (in, flags_save); 107 if (status && !win32_service_interrupt (&win32_signal)) 108 return true; 109 } 110 111 return false; 112} 113 114#endif 115 116#ifdef HAVE_GETPASS 117 118static FILE * 119open_tty (const bool write) 120{ 121 FILE *ret; 122 ret = fopen ("/dev/tty", write ? "w" : "r"); 123 if (!ret) 124 ret = write ? stderr : stdin; 125 return ret; 126} 127 128static void 129close_tty (FILE *fp) 130{ 131 if (fp != stderr && fp != stdin) 132 fclose (fp); 133} 134 135#endif 136 137#ifdef ENABLE_SYSTEMD 138 139/* 140 * is systemd running 141 */ 142 143static bool 144check_systemd_running () 145{ 146 struct stat a, b; 147 148 /* We simply test whether the systemd cgroup hierarchy is 149 * mounted */ 150 151 return (lstat("/sys/fs/cgroup", &a) == 0) 152 && (lstat("/sys/fs/cgroup/systemd", &b) == 0) 153 && (a.st_dev != b.st_dev); 154 155} 156 157static bool 158get_console_input_systemd (const char *prompt, const bool echo, char *input, const int capacity) 159{ 160 int std_out; 161 bool ret = false; 162 struct argv argv; 163 164 argv_init (&argv); 165 argv_printf (&argv, "/bin/systemd-ask-password"); 166 argv_printf_cat (&argv, "%s", prompt); 167 168 if ((std_out = openvpn_popen (&argv, NULL)) < 0) { 169 return false; 170 } 171 CLEAR (*input); 172 if (read (std_out, input, capacity) != 0) 173 { 174 chomp (input); 175 ret = true; 176 } 177 close (std_out); 178 179 argv_reset (&argv); 180 181 return ret; 182} 183 184 185#endif 186 187/* 188 * Get input from console 189 */ 190bool 191get_console_input (const char *prompt, const bool echo, char *input, const int capacity) 192{ 193 bool ret = false; 194 ASSERT (prompt); 195 ASSERT (input); 196 ASSERT (capacity > 0); 197 input[0] = '\0'; 198 199#ifdef ENABLE_SYSTEMD 200 if (check_systemd_running ()) 201 return get_console_input_systemd (prompt, echo, input, capacity); 202#endif 203 204#if defined(WIN32) 205 return get_console_input_win32 (prompt, echo, input, capacity); 206#elif defined(HAVE_GETPASS) 207 if (echo) 208 { 209 FILE *fp; 210 211 fp = open_tty (true); 212 fprintf (fp, "%s", prompt); 213 fflush (fp); 214 close_tty (fp); 215 216 fp = open_tty (false); 217 if (fgets (input, capacity, fp) != NULL) 218 { 219 chomp (input); 220 ret = true; 221 } 222 close_tty (fp); 223 } 224 else 225 { 226 char *gp = getpass (prompt); 227 if (gp) 228 { 229 strncpynt (input, gp, capacity); 230 memset (gp, 0, strlen (gp)); 231 ret = true; 232 } 233 } 234#else 235 msg (M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); 236#endif 237 return ret; 238} 239