apr_getpass.c revision 269847
1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* apr_password_get.c: abstraction to provide for obtaining a password from the 18 * command line in whatever way the OS supports. In the best case, it's a 19 * wrapper for the system library's getpass() routine; otherwise, we 20 * use one we define ourselves. 21 */ 22#include "apr_private.h" 23#include "apr_strings.h" 24#include "apr_lib.h" 25#include "apr_errno.h" 26#if APR_HAVE_SYS_TYPES_H 27#include <sys/types.h> 28#endif 29#if APR_HAVE_ERRNO_H 30#include <errno.h> 31#endif 32 33#if APR_HAVE_UNISTD_H 34#include <unistd.h> 35#endif 36#if APR_HAVE_CONIO_H 37#ifdef _MSC_VER 38#pragma warning(disable: 4032) 39#include <conio.h> 40#pragma warning(default: 4032) 41#else 42#include <conio.h> 43#endif 44#endif 45#if APR_HAVE_STDLIB_H 46#include <stdlib.h> 47#endif 48#if APR_HAVE_STRING_H 49#include <string.h> 50#endif 51#if APR_HAVE_STRINGS_H 52#include <strings.h> 53#endif 54#if APR_HAVE_STDIO_H 55#include <stdio.h> 56#endif 57 58/* Disable getpass() support when PASS_MAX is defined and is "small", 59 * for an arbitrary definition of "small". 60 * HP-UX truncates passwords (PR49496) so we disable getpass() for 61 * this platform too. 62 */ 63#if defined(HAVE_GETPASS) && \ 64 (defined(PASS_MAX) && PASS_MAX < 32) || defined(__hpux) || defined(__hpux__) 65#undef HAVE_GETPASS 66#endif 67 68#if defined(HAVE_TERMIOS_H) && !defined(HAVE_GETPASS) 69#include <termios.h> 70#endif 71 72#if !APR_CHARSET_EBCDIC 73#define LF 10 74#define CR 13 75#else /* APR_CHARSET_EBCDIC */ 76#define LF '\n' 77#define CR '\r' 78#endif /* APR_CHARSET_EBCDIC */ 79 80#define MAX_STRING_LEN 256 81 82#define ERR_OVERFLOW 5 83 84#if !defined(HAVE_GETPASS) && !defined(HAVE_GETPASSPHRASE) && !defined(HAVE_GETPASS_R) 85 86/* MPE, Win32, and BeOS all lack a native getpass() */ 87 88#if !defined(HAVE_TERMIOS_H) && !defined(WIN32) 89/* 90 * MPE lacks getpass() and a way to suppress stdin echo. So for now, just 91 * issue the prompt and read the results with echo. (Ugh). 92 */ 93 94static char *get_password(const char *prompt) 95{ 96 static char password[MAX_STRING_LEN]; 97 98 fputs(prompt, stderr); 99 fgets((char *) &password, sizeof(password), stdin); 100 101 return (char *) &password; 102} 103 104#elif defined(WIN32) 105 106/* 107 * Windows lacks getpass(). So we'll re-implement it here. 108 */ 109 110static char *get_password(const char *prompt) 111{ 112/* WCE lacks console. So the getpass is unsuported 113 * The only way is to use the GUI so the getpass should be implemented 114 * on per-application basis. 115 */ 116#ifdef _WIN32_WCE 117 return NULL; 118#else 119 static char password[128]; 120 int n = 0; 121 int ch; 122 123 fputs(prompt, stderr); 124 125 while ((ch = _getch()) != '\r') { 126 if (ch == EOF) /* EOF */ { 127 fputs("[EOF]\n", stderr); 128 return NULL; 129 } 130 else if (ch == 0 || ch == 0xE0) { 131 /* FN Keys (0 or E0) are a sentinal for a FN code */ 132 ch = (ch << 4) | _getch(); 133 /* Catch {DELETE}, {<--}, Num{DEL} and Num{<--} */ 134 if ((ch == 0xE53 || ch == 0xE4B || ch == 0x053 || ch == 0x04b) && n) { 135 password[--n] = '\0'; 136 fputs("\b \b", stderr); 137 } 138 else { 139 fputc('\a', stderr); 140 } 141 } 142 else if ((ch == '\b' || ch == 127) && n) /* BS/DEL */ { 143 password[--n] = '\0'; 144 fputs("\b \b", stderr); 145 } 146 else if (ch == 3) /* CTRL+C */ { 147 /* _getch() bypasses Ctrl+C but not Ctrl+Break detection! */ 148 fputs("^C\n", stderr); 149 exit(-1); 150 } 151 else if (ch == 26) /* CTRL+Z */ { 152 fputs("^Z\n", stderr); 153 return NULL; 154 } 155 else if (ch == 27) /* ESC */ { 156 fputc('\n', stderr); 157 fputs(prompt, stderr); 158 n = 0; 159 } 160 else if ((n < sizeof(password) - 1) && !apr_iscntrl(ch)) { 161 password[n++] = ch; 162 fputc('*', stderr); 163 } 164 else { 165 fputc('\a', stderr); 166 } 167 } 168 169 fputc('\n', stderr); 170 password[n] = '\0'; 171 return password; 172#endif 173} 174 175#elif defined (HAVE_TERMIOS_H) 176 177static char *get_password(const char *prompt) 178{ 179 struct termios attr; 180 static char password[MAX_STRING_LEN]; 181 int n=0; 182 fputs(prompt, stderr); 183 fflush(stderr); 184 185 if (tcgetattr(STDIN_FILENO, &attr) != 0) 186 return NULL; 187 attr.c_lflag &= ~(ECHO); 188 189 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) != 0) 190 return NULL; 191 while ((password[n] = getchar()) != '\n') { 192 if (n < sizeof(password) - 1 && password[n] >= ' ' && password[n] <= '~') { 193 n++; 194 } else { 195 fprintf(stderr,"\n"); 196 fputs(prompt, stderr); 197 fflush(stderr); 198 n = 0; 199 } 200 } 201 202 password[n] = '\0'; 203 printf("\n"); 204 if (n > (MAX_STRING_LEN - 1)) { 205 password[MAX_STRING_LEN - 1] = '\0'; 206 } 207 208 attr.c_lflag |= ECHO; 209 tcsetattr(STDIN_FILENO, TCSANOW, &attr); 210 return (char*) &password; 211} 212 213#endif /* no getchar or _getch */ 214 215#endif /* no getpass or getpassphrase or getpass_r */ 216 217/* 218 * Use the OS getpass() routine (or our own) to obtain a password from 219 * the input stream. 220 * 221 * Exit values: 222 * 0: Success 223 * 5: Partial success; entered text truncated to the size of the 224 * destination buffer 225 * 226 * Restrictions: Truncation also occurs according to the host system's 227 * getpass() semantics, or at position 255 if our own version is used, 228 * but the caller is *not* made aware of it unless their own buffer is 229 * smaller than our own. 230 */ 231 232APR_DECLARE(apr_status_t) apr_password_get(const char *prompt, char *pwbuf, apr_size_t *bufsiz) 233{ 234 apr_status_t rv = APR_SUCCESS; 235#if defined(HAVE_GETPASS_R) 236 if (getpass_r(prompt, pwbuf, *bufsiz) == NULL) 237 return APR_EINVAL; 238#else 239#if defined(HAVE_GETPASSPHRASE) 240 char *pw_got = getpassphrase(prompt); 241#elif defined(HAVE_GETPASS) 242 char *pw_got = getpass(prompt); 243#else /* use the replacement implementation above */ 244 char *pw_got = get_password(prompt); 245#endif 246 247 if (!pw_got) 248 return APR_EINVAL; 249 if (strlen(pw_got) >= *bufsiz) { 250 rv = APR_ENAMETOOLONG; 251 } 252 apr_cpystrn(pwbuf, pw_got, *bufsiz); 253 memset(pw_got, 0, strlen(pw_got)); 254#endif /* HAVE_GETPASS_R */ 255 return rv; 256} 257