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 char* ptr = NULL;
196 size_t ptr_len = 0;
197
198 const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp);
199 if (res < 0)
200 goto error;
201 replace_char(ptr, ptr_len, "\r\n");
202
203 strncpy(buf, ptr, MIN(bufsiz, ptr_len));
204 free(ptr);
205 if (terminal_needs_reset)
206 {
207 if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1)
208 goto error;
209 }
210
211 if (terminal_fildes != STDIN_FILENO)
212 (void)fclose(fp);
213
214 return buf;
215
216error:
217{
218 // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
219 int saved_errno = errno;
220 if (terminal_needs_reset)
221 (void)tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
222
223 if (terminal_fildes != STDIN_FILENO)
224 {
225 if (fp)
226 (void)fclose(fp);
227 }
228 // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
229 errno = saved_errno;
230}
231
232 return NULL;
233}
234
235static const char* freerdp_passphrase_read_askpass(const char* prompt, char* buf, size_t bufsiz,
236 char const* askpass_env)
237{
238 char command[4096] = { 0 };
239
240 (void)sprintf_s(command, sizeof(command), "%s 'FreeRDP authentication\n%s'", askpass_env,
241 prompt);
242 // NOLINTNEXTLINE(clang-analyzer-optin.taint.GenericTaint)
243 FILE* askproc = popen(command, "r");
244 if (!askproc)
245 return NULL;
246 WINPR_ASSERT(bufsiz <= INT32_MAX);
247 if (fgets(buf, (int)bufsiz, askproc) != NULL)
248 buf[strcspn(buf, "\r\n")] = '\0';
249 else
250 buf = NULL;
251 const int status = pclose(askproc);
252 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
253 buf = NULL;
254
255 return buf;
256}
257
258const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
259 size_t bufsiz, int from_stdin)
260{
261 // NOLINTNEXTLINE(concurrency-mt-unsafe)
262 const char* askpass_env = getenv("FREERDP_ASKPASS");
263
264 if (askpass_env)
265 return freerdp_passphrase_read_askpass(prompt, buf, bufsiz, askpass_env);
266 else
267 return freerdp_passphrase_read_tty(context, prompt, buf, bufsiz, from_stdin);
268}
269
270static BOOL set_termianl_nonblock(int ifd, BOOL nonblock);
271
272static void restore_terminal(void)
273{
274 (void)set_termianl_nonblock(-1, FALSE);
275}
276
277BOOL set_termianl_nonblock(int ifd, BOOL nonblock)
278{
279 static int fd = -1;
280 static bool registered = false;
281 static int orig = 0;
282 static struct termios termios = { 0 };
283
284 if (ifd >= 0)
285 fd = ifd;
286
287 if (fd < 0)
288 return FALSE;
289
290 if (nonblock)
291 {
292 if (!registered)
293 {
294 (void)atexit(restore_terminal);
295 registered = true;
296 }
297
298 const int rc1 = fcntl(fd, F_SETFL, orig | O_NONBLOCK);
299 if (rc1 != 0)
300 {
301 char buffer[128] = { 0 };
302 WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
303 winpr_strerror(errno, buffer, sizeof(buffer)));
304 return FALSE;
305 }
306 const int rc2 = tcgetattr(fd, &termios);
307 if (rc2 != 0)
308 {
309 char buffer[128] = { 0 };
310 WLog_ERR(TAG, "tcgetattr() failed with %s",
311 winpr_strerror(errno, buffer, sizeof(buffer)));
312 return FALSE;
313 }
314
315 struct termios now = termios;
316 cfmakeraw(&now);
317 const int rc3 = tcsetattr(fd, TCSANOW, &now);
318 if (rc3 != 0)
319 {
320 char buffer[128] = { 0 };
321 WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
322 winpr_strerror(errno, buffer, sizeof(buffer)));
323 return FALSE;
324 }
325 }
326 else
327 {
328 const int rc1 = tcsetattr(fd, TCSANOW, &termios);
329 if (rc1 != 0)
330 {
331 char buffer[128] = { 0 };
332 WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
333 winpr_strerror(errno, buffer, sizeof(buffer)));
334 return FALSE;
335 }
336 const int rc2 = fcntl(fd, F_SETFL, orig);
337 if (rc2 != 0)
338 {
339 char buffer[128] = { 0 };
340 WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
341 winpr_strerror(errno, buffer, sizeof(buffer)));
342 return FALSE;
343 }
344 fd = -1;
345 }
346 return TRUE;
347}
348
349int freerdp_interruptible_getc(rdpContext* context, FILE* stream)
350{
351 int rc = EOF;
352 const int fd = fileno(stream);
353
354 (void)set_termianl_nonblock(fd, TRUE);
355
356 do
357 {
358 const int res = wait_for_fd(fd, 10);
359 if (res != 0)
360 {
361 char c = 0;
362 const ssize_t rd = read(fd, &c, 1);
363 if (rd == 1)
364 {
365 if (c == 3) /* ctrl + c */
366 return EOF;
367 if (c == 4) /* ctrl + d */
368 return EOF;
369 if (c == 26) /* ctrl + z */
370 return EOF;
371 rc = (int)c;
372 }
373 break;
374 }
375 } while (!freerdp_shall_disconnect_context(context));
376
377 (void)set_termianl_nonblock(fd, FALSE);
378
379 return rc;
380}
381
382#else
383
384const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
385 size_t bufsiz, int from_stdin)
386{
387 return NULL;
388}
389
390int freerdp_interruptible_getc(rdpContext* context, FILE* f)
391{
392 return EOF;
393}
394#endif
395
396SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
397 FILE* stream)
398{
399 int c = 0;
400 char* n = NULL;
401 size_t step = 32;
402 size_t used = 0;
403 char* ptr = NULL;
404 size_t len = 0;
405
406 if (!plineptr || !psize)
407 {
408 errno = EINVAL;
409 return -1;
410 }
411
412 bool echo = true;
413#if !defined(_WIN32) && !defined(ANDROID)
414 {
415 const int fd = fileno(stream);
416
417 struct termios termios = { 0 };
418 /* This might fail if /from-stdin is used. */
419 if (tcgetattr(fd, &termios) == 0)
420 echo = (termios.c_lflag & ECHO) != 0;
421 else
422 echo = false;
423 }
424#endif
425
426 if (*plineptr && (*psize > 0))
427 {
428 ptr = *plineptr;
429 used = *psize;
430 if (echo)
431 {
432 printf("%s", ptr);
433 (void)fflush(stdout);
434 }
435 }
436
437 do
438 {
439 if (used + 2 >= len)
440 {
441 len += step;
442 n = realloc(ptr, len);
443
444 if (!n)
445 {
446 return -1;
447 }
448
449 ptr = n;
450 }
451
452 c = freerdp_interruptible_getc(context, stream);
453 if (c == 127)
454 {
455 if (used > 0)
456 {
457 ptr[used--] = '\0';
458 if (echo)
459 {
460 printf("\b");
461 printf(" ");
462 printf("\b");
463 (void)fflush(stdout);
464 }
465 }
466 continue;
467 }
468 if (echo)
469 {
470 printf("%c", c);
471 (void)fflush(stdout);
472 }
473 if (c != EOF)
474 ptr[used++] = (char)c;
475 } while ((c != '\n') && (c != '\r') && (c != EOF));
476
477 printf("\n");
478 ptr[used] = '\0';
479 if (c == EOF)
480 {
481 free(ptr);
482 return EOF;
483 }
484 *plineptr = ptr;
485 *psize = used;
486 return WINPR_ASSERTING_INT_CAST(SSIZE_T, used);
487}