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