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