FreeRDP
Loading...
Searching...
No Matches
passphrase.c
1
20#include <winpr/atexit.h>
21#include <winpr/environment.h>
22
23#include <freerdp/config.h>
24#include <freerdp/freerdp.h>
25
26#include <errno.h>
27#include <freerdp/utils/passphrase.h>
28
29#ifdef _WIN32
30
31#include <stdio.h>
32#include <io.h>
33#include <conio.h>
34#include <wincred.h>
35
36static char read_chr(FILE* f)
37{
38 char chr;
39 const BOOL isTty = _isatty(_fileno(f));
40 if (isTty)
41 return fgetc(f);
42 if (fscanf_s(f, "%c", &chr, (UINT32)sizeof(char)) && !feof(f))
43 return chr;
44 return 0;
45}
46
47int freerdp_interruptible_getc(rdpContext* context, FILE* f)
48{
49 return read_chr(f);
50}
51
52const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
53 size_t bufsiz, int from_stdin)
54{
55 WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 'p', 'r', 'e', 'f', 'i',
56 'l', 'l', 'e', 'd', '\0' };
57 WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = WINPR_C_ARRAY_INIT;
58 BOOL fSave = FALSE;
59 DWORD dwFlags = 0;
60 WCHAR* promptW = ConvertUtf8ToWCharAlloc(prompt, nullptr);
61 const DWORD status =
62 CredUICmdLinePromptForCredentialsW(promptW, nullptr, 0, UserNameW, ARRAYSIZE(UserNameW),
63 PasswordW, ARRAYSIZE(PasswordW), &fSave, dwFlags);
64 free(promptW);
65 if (ConvertWCharNToUtf8(PasswordW, ARRAYSIZE(PasswordW), buf, bufsiz) < 0)
66 return nullptr;
67 return buf;
68}
69
70#elif !defined(ANDROID)
71
72#include <fcntl.h>
73#include <stdio.h>
74#include <string.h>
75#include <sys/stat.h>
76#include <sys/wait.h>
77#include <termios.h>
78#include <unistd.h>
79#include <freerdp/utils/signal.h>
80#include <freerdp/log.h>
81#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
82#include <poll.h>
83#else
84#include <time.h>
85#include <sys/select.h>
86#endif
87
88#define TAG FREERDP_TAG("utils.passphrase")
89
90static int wait_for_fd(int fd, int timeout)
91{
92 int status = 0;
93#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
94 struct pollfd pollset = WINPR_C_ARRAY_INIT;
95 pollset.fd = fd;
96 pollset.events = POLLIN;
97 pollset.revents = 0;
98
99 do
100 {
101 status = poll(&pollset, 1, timeout);
102 } while ((status < 0) && (errno == EINTR));
103
104#else
105 fd_set rset = WINPR_C_ARRAY_INIT;
106 struct timeval tv = WINPR_C_ARRAY_INIT;
107 FD_ZERO(&rset);
108 FD_SET(fd, &rset);
109
110 if (timeout)
111 {
112 tv.tv_sec = timeout / 1000;
113 tv.tv_usec = (timeout % 1000) * 1000;
114 }
115
116 do
117 {
118 status = select(fd + 1, &rset, nullptr, nullptr, timeout ? &tv : nullptr);
119 } while ((status < 0) && (errno == EINTR));
120
121#endif
122 return status;
123}
124
125static void replace_char(char* buffer, WINPR_ATTR_UNUSED size_t buffer_len, const char* toreplace)
126{
127 while (*toreplace != '\0')
128 {
129 char* ptr = nullptr;
130 while ((ptr = strrchr(buffer, *toreplace)) != nullptr)
131 *ptr = '\0';
132 toreplace++;
133 }
134}
135
136static const char* freerdp_passphrase_read_tty(rdpContext* context, const char* prompt, char* buf,
137 size_t bufsiz, int from_stdin)
138{
139 BOOL terminal_needs_reset = FALSE;
140 char term_name[L_ctermid] = WINPR_C_ARRAY_INIT;
141
142 FILE* fout = nullptr;
143
144 if (bufsiz == 0)
145 {
146 errno = EINVAL;
147 return nullptr;
148 }
149
150 ctermid(term_name);
151 int terminal_fildes = 0;
152 if (from_stdin || (strcmp(term_name, "") == 0))
153 {
154 fout = stdout;
155 terminal_fildes = STDIN_FILENO;
156 }
157 else
158 {
159 const int term_file = open(term_name, O_RDWR);
160 if (term_file < 0)
161 {
162 fout = stdout;
163 terminal_fildes = STDIN_FILENO;
164 }
165 else
166 {
167 fout = fdopen(term_file, "w");
168 if (!fout)
169 {
170 close(term_file);
171 return nullptr;
172 }
173 terminal_fildes = term_file;
174 }
175 }
176
177 struct termios orig_flags = WINPR_C_ARRAY_INIT;
178 if (tcgetattr(terminal_fildes, &orig_flags) != -1)
179 {
180 struct termios new_flags = WINPR_C_ARRAY_INIT;
181 new_flags = orig_flags;
182 new_flags.c_lflag &= (uint32_t)~ECHO;
183 new_flags.c_lflag |= ECHONL;
184 terminal_needs_reset = TRUE;
185 if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1)
186 terminal_needs_reset = FALSE;
187 }
188
189 FILE* fp = fdopen(terminal_fildes, "r");
190 if (!fp)
191 goto error;
192
193 (void)fprintf(fout, "%s", prompt);
194 (void)fflush(fout);
195
196 {
197 char* ptr = nullptr;
198 size_t ptr_len = 0;
199 const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp);
200 if (res < 0)
201 goto error;
202
203 replace_char(ptr, ptr_len, "\r\n");
204
205 strncpy(buf, ptr, MIN(bufsiz, ptr_len));
206 free(ptr);
207 }
208
209 if (terminal_needs_reset)
210 {
211 if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1)
212 goto error;
213 }
214
215 if (terminal_fildes != STDIN_FILENO)
216 (void)fclose(fp);
217
218 return buf;
219
220error:
221{
222 // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
223 int saved_errno = errno;
224 if (terminal_needs_reset)
225 (void)tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
226
227 if (terminal_fildes != STDIN_FILENO)
228 {
229 if (fp)
230 (void)fclose(fp);
231 }
232 // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
233 errno = saved_errno;
234}
235
236 return nullptr;
237}
238
239static const char* freerdp_passphrase_read_askpass(const char* prompt, char* buf, size_t bufsiz,
240 char const* askpass_env)
241{
242 char command[4096] = WINPR_C_ARRAY_INIT;
243
244 (void)sprintf_s(command, sizeof(command), "%s 'FreeRDP authentication\n%s'", askpass_env,
245 prompt);
246 // NOLINTNEXTLINE(clang-analyzer-optin.taint.GenericTaint)
247 FILE* askproc = popen(command, "r");
248 if (!askproc)
249 return nullptr;
250 WINPR_ASSERT(bufsiz <= INT32_MAX);
251 if (fgets(buf, (int)bufsiz, askproc) != nullptr)
252 buf[strcspn(buf, "\r\n")] = '\0';
253 else
254 buf = nullptr;
255 const int status = pclose(askproc);
256 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
257 buf = nullptr;
258
259 return buf;
260}
261
262const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
263 size_t bufsiz, int from_stdin)
264{
265 // NOLINTNEXTLINE(concurrency-mt-unsafe)
266 const char* askpass_env = getenv("FREERDP_ASKPASS");
267
268 if (askpass_env)
269 return freerdp_passphrase_read_askpass(prompt, buf, bufsiz, askpass_env);
270 else
271 return freerdp_passphrase_read_tty(context, prompt, buf, bufsiz, from_stdin);
272}
273
274static BOOL set_termianl_nonblock(int ifd, BOOL nonblock);
275
276static void restore_terminal(void)
277{
278 (void)set_termianl_nonblock(-1, FALSE);
279}
280
281BOOL set_termianl_nonblock(int ifd, BOOL nonblock)
282{
283 static int fd = -1;
284 static bool registered = false;
285 static int orig = 0;
286 static struct termios termios = WINPR_C_ARRAY_INIT;
287
288 if (ifd >= 0)
289 fd = ifd;
290
291 if (fd < 0)
292 return FALSE;
293
294 if (nonblock)
295 {
296 if (!registered)
297 {
298 (void)winpr_atexit(restore_terminal);
299 registered = true;
300 }
301
302 const int rc1 = fcntl(fd, F_SETFL, orig | O_NONBLOCK);
303 if (rc1 != 0)
304 {
305 char buffer[128] = WINPR_C_ARRAY_INIT;
306 WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
307 winpr_strerror(errno, buffer, sizeof(buffer)));
308 return FALSE;
309 }
310 const int rc2 = tcgetattr(fd, &termios);
311 if (rc2 != 0)
312 {
313 char buffer[128] = WINPR_C_ARRAY_INIT;
314 WLog_ERR(TAG, "tcgetattr() failed with %s",
315 winpr_strerror(errno, buffer, sizeof(buffer)));
316 return FALSE;
317 }
318
319 struct termios now = termios;
320 cfmakeraw(&now);
321 const int rc3 = tcsetattr(fd, TCSANOW, &now);
322 if (rc3 != 0)
323 {
324 char buffer[128] = WINPR_C_ARRAY_INIT;
325 WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
326 winpr_strerror(errno, buffer, sizeof(buffer)));
327 return FALSE;
328 }
329 }
330 else
331 {
332 const int rc1 = tcsetattr(fd, TCSANOW, &termios);
333 if (rc1 != 0)
334 {
335 char buffer[128] = WINPR_C_ARRAY_INIT;
336 WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
337 winpr_strerror(errno, buffer, sizeof(buffer)));
338 return FALSE;
339 }
340 const int rc2 = fcntl(fd, F_SETFL, orig);
341 if (rc2 != 0)
342 {
343 char buffer[128] = WINPR_C_ARRAY_INIT;
344 WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
345 winpr_strerror(errno, buffer, sizeof(buffer)));
346 return FALSE;
347 }
348 fd = -1;
349 }
350 return TRUE;
351}
352
353int freerdp_interruptible_getc(rdpContext* context, FILE* stream)
354{
355 int rc = EOF;
356 const int fd = fileno(stream);
357
358 (void)set_termianl_nonblock(fd, TRUE);
359
360 do
361 {
362 const int res = wait_for_fd(fd, 10);
363 if (res != 0)
364 {
365 char c = 0;
366 const ssize_t rd = read(fd, &c, 1);
367 if (rd == 1)
368 {
369 if (c == 3) /* ctrl + c */
370 return EOF;
371 if (c == 4) /* ctrl + d */
372 return EOF;
373 if (c == 26) /* ctrl + z */
374 return EOF;
375 rc = (int)c;
376 }
377 break;
378 }
379 } while (!freerdp_shall_disconnect_context(context));
380
381 (void)set_termianl_nonblock(fd, FALSE);
382
383 return rc;
384}
385
386#else
387
388const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
389 size_t bufsiz, int from_stdin)
390{
391 return nullptr;
392}
393
394int freerdp_interruptible_getc(rdpContext* context, FILE* f)
395{
396 return EOF;
397}
398#endif
399
400SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
401 FILE* stream)
402{
403 int c = 0;
404 char* n = nullptr;
405 size_t step = 32;
406 size_t used = 0;
407 char* ptr = nullptr;
408 size_t len = 0;
409
410 if (!plineptr || !psize)
411 {
412 errno = EINVAL;
413 return -1;
414 }
415
416 bool echo = true;
417#if !defined(_WIN32) && !defined(ANDROID)
418 {
419 const int fd = fileno(stream);
420
421 struct termios termios = WINPR_C_ARRAY_INIT;
422 /* This might fail if /from-stdin is used. */
423 if (tcgetattr(fd, &termios) == 0)
424 echo = (termios.c_lflag & ECHO) != 0;
425 else
426 echo = false;
427 }
428#endif
429
430 if (*plineptr && (*psize > 0))
431 {
432 ptr = *plineptr;
433 used = *psize;
434 if (echo)
435 {
436 printf("%s", ptr);
437 (void)fflush(stdout);
438 }
439 }
440
441 do
442 {
443 if (used + 2 >= len)
444 {
445 len += step;
446 n = realloc(ptr, len);
447
448 if (!n)
449 {
450 free(ptr);
451 *plineptr = nullptr;
452 return -1;
453 }
454
455 ptr = n;
456 }
457
458 c = freerdp_interruptible_getc(context, stream);
459 if (c == 127)
460 {
461 if (used > 0)
462 {
463 ptr[used--] = '\0';
464 if (echo)
465 {
466 printf("\b");
467 printf(" ");
468 printf("\b");
469 (void)fflush(stdout);
470 }
471 }
472 continue;
473 }
474 if (echo)
475 {
476 printf("%c", c);
477 (void)fflush(stdout);
478 }
479 if (c != EOF)
480 ptr[used++] = (char)c;
481 } while ((c != '\n') && (c != '\r') && (c != EOF));
482
483 printf("\n");
484 ptr[used] = '\0';
485 if (c == EOF)
486 {
487 free(ptr);
488 *plineptr = nullptr;
489 return EOF;
490 }
491 *plineptr = ptr;
492 *psize = used;
493 return WINPR_ASSERTING_INT_CAST(SSIZE_T, used);
494}