FreeRDP
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 
35 static 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 
46 int freerdp_interruptible_getc(rdpContext* context, FILE* f)
47 {
48  return read_chr(f);
49 }
50 
51 const 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 <termios.h>
76 #include <unistd.h>
77 #include <freerdp/utils/signal.h>
78 
79 #if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
80 #include <poll.h>
81 #else
82 #include <time.h>
83 #include <sys/select.h>
84 #endif
85 
86 static int wait_for_fd(int fd, int timeout)
87 {
88  int status = 0;
89 #if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
90  struct pollfd pollset = { 0 };
91  pollset.fd = fd;
92  pollset.events = POLLIN;
93  pollset.revents = 0;
94 
95  do
96  {
97  status = poll(&pollset, 1, timeout);
98  } while ((status < 0) && (errno == EINTR));
99 
100 #else
101  fd_set rset = { 0 };
102  struct timeval tv = { 0 };
103  FD_ZERO(&rset);
104  FD_SET(fd, &rset);
105 
106  if (timeout)
107  {
108  tv.tv_sec = timeout / 1000;
109  tv.tv_usec = (timeout % 1000) * 1000;
110  }
111 
112  do
113  {
114  status = select(fd + 1, &rset, NULL, NULL, timeout ? &tv : NULL);
115  } while ((status < 0) && (errno == EINTR));
116 
117 #endif
118  return status;
119 }
120 
121 static void replace_char(char* buffer, size_t buffer_len, const char* toreplace)
122 {
123  while (*toreplace != '\0')
124  {
125  char* ptr = NULL;
126  while ((ptr = strrchr(buffer, *toreplace)) != NULL)
127  *ptr = '\0';
128  toreplace++;
129  }
130 }
131 
132 static const char* freerdp_passphrase_read_tty(rdpContext* context, const char* prompt, char* buf,
133  size_t bufsiz, int from_stdin)
134 {
135  BOOL terminal_needs_reset = FALSE;
136  char term_name[L_ctermid] = { 0 };
137 
138  FILE* fout = NULL;
139 
140  if (bufsiz == 0)
141  {
142  errno = EINVAL;
143  return NULL;
144  }
145 
146  ctermid(term_name);
147  int terminal_fildes = 0;
148  if (from_stdin || (strcmp(term_name, "") == 0))
149  {
150  fout = stdout;
151  terminal_fildes = STDIN_FILENO;
152  }
153  else
154  {
155  const int term_file = open(term_name, O_RDWR);
156  if (term_file < 0)
157  {
158  fout = stdout;
159  terminal_fildes = STDIN_FILENO;
160  }
161  else
162  {
163  fout = fdopen(term_file, "w");
164  if (!fout)
165  {
166  close(term_file);
167  return NULL;
168  }
169  terminal_fildes = term_file;
170  }
171  }
172 
173  struct termios orig_flags = { 0 };
174  if (tcgetattr(terminal_fildes, &orig_flags) != -1)
175  {
176  struct termios new_flags = { 0 };
177  new_flags = orig_flags;
178  new_flags.c_lflag &= (uint32_t)~ECHO;
179  new_flags.c_lflag |= ECHONL;
180  terminal_needs_reset = TRUE;
181  if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1)
182  terminal_needs_reset = FALSE;
183  }
184 
185  FILE* fp = fdopen(terminal_fildes, "r");
186  if (!fp)
187  goto error;
188 
189  (void)fprintf(fout, "%s", prompt);
190  (void)fflush(fout);
191 
192  char* ptr = NULL;
193  size_t ptr_len = 0;
194 
195  const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp);
196  if (res < 0)
197  goto error;
198  replace_char(ptr, ptr_len, "\r\n");
199 
200  strncpy(buf, ptr, MIN(bufsiz, ptr_len));
201  free(ptr);
202  if (terminal_needs_reset)
203  {
204  if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1)
205  goto error;
206  }
207 
208  if (terminal_fildes != STDIN_FILENO)
209  {
210  if (fclose(fp) == -1)
211  goto error;
212  }
213 
214  return buf;
215 
216 error:
217 {
218  int saved_errno = errno;
219  if (terminal_needs_reset)
220  (void)tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
221 
222  if (terminal_fildes != STDIN_FILENO)
223  {
224  if (fp)
225  (void)fclose(fp);
226  }
227  errno = saved_errno;
228  return NULL;
229 }
230 }
231 
232 static const char* freerdp_passphrase_read_askpass(const char* prompt, char* buf, size_t bufsiz,
233  char const* askpass_env)
234 {
235  char command[4096] = { 0 };
236 
237  (void)sprintf_s(command, sizeof(command), "%s 'FreeRDP authentication\n%s'", askpass_env,
238  prompt);
239  // NOLINTNEXTLINE(clang-analyzer-optin.taint.GenericTaint)
240  FILE* askproc = popen(command, "r");
241  if (!askproc)
242  return NULL;
243  WINPR_ASSERT(bufsiz <= INT32_MAX);
244  if (fgets(buf, (int)bufsiz, askproc) != NULL)
245  buf[strcspn(buf, "\r\n")] = '\0';
246  else
247  buf = NULL;
248  const int status = pclose(askproc);
249  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
250  buf = NULL;
251 
252  return buf;
253 }
254 
255 const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
256  size_t bufsiz, int from_stdin)
257 {
258  const char* askpass_env = getenv("FREERDP_ASKPASS");
259 
260  if (askpass_env)
261  return freerdp_passphrase_read_askpass(prompt, buf, bufsiz, askpass_env);
262  else
263  return freerdp_passphrase_read_tty(context, prompt, buf, bufsiz, from_stdin);
264 }
265 
266 int freerdp_interruptible_getc(rdpContext* context, FILE* f)
267 {
268  int rc = EOF;
269  const int fd = fileno(f);
270 
271  const int orig = fcntl(fd, F_GETFL);
272  (void)fcntl(fd, F_SETFL, orig | O_NONBLOCK);
273  do
274  {
275  const int res = wait_for_fd(fd, 10);
276  if (res != 0)
277  {
278  char c = 0;
279  const ssize_t rd = read(fd, &c, 1);
280  if (rd == 1)
281  rc = (int)c;
282  break;
283  }
284  } while (!freerdp_shall_disconnect_context(context));
285 
286  (void)fcntl(fd, F_SETFL, orig);
287  return rc;
288 }
289 
290 #else
291 
292 const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
293  size_t bufsiz, int from_stdin)
294 {
295  return NULL;
296 }
297 
298 int freerdp_interruptible_getc(rdpContext* context, FILE* f)
299 {
300  return EOF;
301 }
302 #endif
303 
304 SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
305  FILE* stream)
306 {
307  int c = 0;
308  char* n = NULL;
309  size_t step = 32;
310  size_t used = 0;
311  char* ptr = NULL;
312  size_t len = 0;
313 
314  if (!plineptr || !psize)
315  {
316  errno = EINVAL;
317  return -1;
318  }
319 
320  do
321  {
322  if (used + 2 >= len)
323  {
324  len += step;
325  n = realloc(ptr, len);
326 
327  if (!n)
328  {
329  return -1;
330  }
331 
332  ptr = n;
333  }
334 
335  c = freerdp_interruptible_getc(context, stream);
336  if (c != EOF)
337  ptr[used++] = (char)c;
338  } while ((c != '\n') && (c != '\r') && (c != EOF));
339 
340  ptr[used] = '\0';
341  if (c == EOF)
342  {
343  free(ptr);
344  return EOF;
345  }
346  *plineptr = ptr;
347  *psize = used;
348  return WINPR_ASSERTING_INT_CAST(SSIZE_T, used);
349 }