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