Coverage Report

Created: 2024-09-08 06:20

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