Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/utils/passphrase.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Passphrase Handling Utils
4
 *
5
 * Copyright 2011 Shea Levy <shea@shealevy.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
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 <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
87
static int wait_for_fd(int fd, int timeout)
88
0
{
89
0
  int status = 0;
90
0
#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
91
0
  struct pollfd pollset = { 0 };
92
0
  pollset.fd = fd;
93
0
  pollset.events = POLLIN;
94
0
  pollset.revents = 0;
95
96
0
  do
97
0
  {
98
0
    status = poll(&pollset, 1, timeout);
99
0
  } 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
0
  return status;
120
0
}
121
122
static void replace_char(char* buffer, WINPR_ATTR_UNUSED size_t buffer_len, const char* toreplace)
123
0
{
124
0
  while (*toreplace != '\0')
125
0
  {
126
0
    char* ptr = NULL;
127
0
    while ((ptr = strrchr(buffer, *toreplace)) != NULL)
128
0
      *ptr = '\0';
129
0
    toreplace++;
130
0
  }
131
0
}
132
133
static const char* freerdp_passphrase_read_tty(rdpContext* context, const char* prompt, char* buf,
134
                                               size_t bufsiz, int from_stdin)
135
0
{
136
0
  BOOL terminal_needs_reset = FALSE;
137
0
  char term_name[L_ctermid] = { 0 };
138
139
0
  FILE* fout = NULL;
140
141
0
  if (bufsiz == 0)
142
0
  {
143
0
    errno = EINVAL;
144
0
    return NULL;
145
0
  }
146
147
0
  ctermid(term_name);
148
0
  int terminal_fildes = 0;
149
0
  if (from_stdin || (strcmp(term_name, "") == 0))
150
0
  {
151
0
    fout = stdout;
152
0
    terminal_fildes = STDIN_FILENO;
153
0
  }
154
0
  else
155
0
  {
156
0
    const int term_file = open(term_name, O_RDWR);
157
0
    if (term_file < 0)
158
0
    {
159
0
      fout = stdout;
160
0
      terminal_fildes = STDIN_FILENO;
161
0
    }
162
0
    else
163
0
    {
164
0
      fout = fdopen(term_file, "w");
165
0
      if (!fout)
166
0
      {
167
0
        close(term_file);
168
0
        return NULL;
169
0
      }
170
0
      terminal_fildes = term_file;
171
0
    }
172
0
  }
173
174
0
  struct termios orig_flags = { 0 };
175
0
  if (tcgetattr(terminal_fildes, &orig_flags) != -1)
176
0
  {
177
0
    struct termios new_flags = { 0 };
178
0
    new_flags = orig_flags;
179
0
    new_flags.c_lflag &= (uint32_t)~ECHO;
180
0
    new_flags.c_lflag |= ECHONL;
181
0
    terminal_needs_reset = TRUE;
182
0
    if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1)
183
0
      terminal_needs_reset = FALSE;
184
0
  }
185
186
0
  FILE* fp = fdopen(terminal_fildes, "r");
187
0
  if (!fp)
188
0
    goto error;
189
190
0
  (void)fprintf(fout, "%s", prompt);
191
0
  (void)fflush(fout);
192
193
0
  char* ptr = NULL;
194
0
  size_t ptr_len = 0;
195
196
0
  const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp);
197
0
  if (res < 0)
198
0
    goto error;
199
0
  replace_char(ptr, ptr_len, "\r\n");
200
201
0
  strncpy(buf, ptr, MIN(bufsiz, ptr_len));
202
0
  free(ptr);
203
0
  if (terminal_needs_reset)
204
0
  {
205
0
    if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1)
206
0
      goto error;
207
0
  }
208
209
0
  if (terminal_fildes != STDIN_FILENO)
210
0
    (void)fclose(fp);
211
212
0
  return buf;
213
214
0
error:
215
0
{
216
  // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
217
0
  int saved_errno = errno;
218
0
  if (terminal_needs_reset)
219
0
    (void)tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
220
221
0
  if (terminal_fildes != STDIN_FILENO)
222
0
  {
223
0
    if (fp)
224
0
      (void)fclose(fp);
225
0
  }
226
  // NOLINTNEXTLINE(clang-analyzer-unix.Stream)
227
0
  errno = saved_errno;
228
0
}
229
230
0
  return NULL;
231
0
}
232
233
static const char* freerdp_passphrase_read_askpass(const char* prompt, char* buf, size_t bufsiz,
234
                                                   char const* askpass_env)
235
0
{
236
0
  char command[4096] = { 0 };
237
238
0
  (void)sprintf_s(command, sizeof(command), "%s 'FreeRDP authentication\n%s'", askpass_env,
239
0
                  prompt);
240
  // NOLINTNEXTLINE(clang-analyzer-optin.taint.GenericTaint)
241
0
  FILE* askproc = popen(command, "r");
242
0
  if (!askproc)
243
0
    return NULL;
244
0
  WINPR_ASSERT(bufsiz <= INT32_MAX);
245
0
  if (fgets(buf, (int)bufsiz, askproc) != NULL)
246
0
    buf[strcspn(buf, "\r\n")] = '\0';
247
0
  else
248
0
    buf = NULL;
249
0
  const int status = pclose(askproc);
250
0
  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
251
0
    buf = NULL;
252
253
0
  return buf;
254
0
}
255
256
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
257
                                    size_t bufsiz, int from_stdin)
258
0
{
259
  // NOLINTNEXTLINE(concurrency-mt-unsafe)
260
0
  const char* askpass_env = getenv("FREERDP_ASKPASS");
261
262
0
  if (askpass_env)
263
0
    return freerdp_passphrase_read_askpass(prompt, buf, bufsiz, askpass_env);
264
0
  else
265
0
    return freerdp_passphrase_read_tty(context, prompt, buf, bufsiz, from_stdin);
266
0
}
267
268
int freerdp_interruptible_getc(rdpContext* context, FILE* f)
269
0
{
270
0
  int rc = EOF;
271
0
  const int fd = fileno(f);
272
273
0
  const int orig = fcntl(fd, F_GETFL);
274
0
  (void)fcntl(fd, F_SETFL, orig | O_NONBLOCK);
275
0
  do
276
0
  {
277
0
    const int res = wait_for_fd(fd, 10);
278
0
    if (res != 0)
279
0
    {
280
0
      char c = 0;
281
0
      const ssize_t rd = read(fd, &c, 1);
282
0
      if (rd == 1)
283
0
        rc = (int)c;
284
0
      break;
285
0
    }
286
0
  } while (!freerdp_shall_disconnect_context(context));
287
288
0
  (void)fcntl(fd, F_SETFL, orig);
289
0
  return rc;
290
0
}
291
292
#else
293
294
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
295
                                    size_t bufsiz, int from_stdin)
296
{
297
  return NULL;
298
}
299
300
int freerdp_interruptible_getc(rdpContext* context, FILE* f)
301
{
302
  return EOF;
303
}
304
#endif
305
306
SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
307
                                       FILE* stream)
308
0
{
309
0
  int c = 0;
310
0
  char* n = NULL;
311
0
  size_t step = 32;
312
0
  size_t used = 0;
313
0
  char* ptr = NULL;
314
0
  size_t len = 0;
315
316
0
  if (!plineptr || !psize)
317
0
  {
318
0
    errno = EINVAL;
319
0
    return -1;
320
0
  }
321
322
0
  do
323
0
  {
324
0
    if (used + 2 >= len)
325
0
    {
326
0
      len += step;
327
0
      n = realloc(ptr, len);
328
329
0
      if (!n)
330
0
      {
331
0
        return -1;
332
0
      }
333
334
0
      ptr = n;
335
0
    }
336
337
0
    c = freerdp_interruptible_getc(context, stream);
338
0
    if (c != EOF)
339
0
      ptr[used++] = (char)c;
340
0
  } while ((c != '\n') && (c != '\r') && (c != EOF));
341
342
0
  ptr[used] = '\0';
343
0
  if (c == EOF)
344
0
  {
345
0
    free(ptr);
346
0
    return EOF;
347
0
  }
348
0
  *plineptr = ptr;
349
0
  *psize = used;
350
0
  return WINPR_ASSERTING_INT_CAST(SSIZE_T, used);
351
0
}