Coverage Report

Created: 2024-05-20 06:11

/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
char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz,
131
                              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
  fprintf(fout, "%s", prompt);
175
0
  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
      fclose(fp);
210
0
  }
211
0
  errno = saved_errno;
212
0
  return NULL;
213
0
}
214
0
}
215
216
int freerdp_interruptible_getc(rdpContext* context, FILE* f)
217
0
{
218
0
  int rc = EOF;
219
0
  const int fd = fileno(f);
220
221
0
  const int orig = fcntl(fd, F_GETFL);
222
0
  fcntl(fd, F_SETFL, orig | O_NONBLOCK);
223
0
  do
224
0
  {
225
0
    const int res = wait_for_fd(fd, 10);
226
0
    if (res != 0)
227
0
    {
228
0
      char c = 0;
229
0
      const ssize_t rd = read(fd, &c, 1);
230
0
      if (rd == 1)
231
0
        rc = c;
232
0
      break;
233
0
    }
234
0
  } while (!freerdp_shall_disconnect_context(context));
235
236
0
  fcntl(fd, F_SETFL, orig);
237
0
  return rc;
238
0
}
239
240
#else
241
242
char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf, size_t bufsiz,
243
                              int from_stdin)
244
{
245
  return NULL;
246
}
247
248
int freerdp_interruptible_getc(rdpContext* context, FILE* f)
249
{
250
  return EOF;
251
}
252
#endif
253
254
SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
255
                                       FILE* stream)
256
0
{
257
0
  int c = 0;
258
0
  char* n = NULL;
259
0
  size_t step = 32;
260
0
  size_t used = 0;
261
0
  char* ptr = NULL;
262
0
  size_t len = 0;
263
264
0
  if (!plineptr || !psize)
265
0
  {
266
0
    errno = EINVAL;
267
0
    return -1;
268
0
  }
269
270
0
  do
271
0
  {
272
0
    if (used + 2 >= len)
273
0
    {
274
0
      len += step;
275
0
      n = realloc(ptr, len);
276
277
0
      if (!n)
278
0
      {
279
0
        return -1;
280
0
      }
281
282
0
      ptr = n;
283
0
    }
284
285
0
    c = freerdp_interruptible_getc(context, stream);
286
0
    if (c != EOF)
287
0
      ptr[used++] = (char)c;
288
0
  } while ((c != '\n') && (c != '\r') && (c != EOF));
289
290
0
  ptr[used] = '\0';
291
0
  if (c == EOF)
292
0
  {
293
0
    free(ptr);
294
0
    return EOF;
295
0
  }
296
0
  *plineptr = ptr;
297
0
  *psize = used;
298
0
  return used;
299
0
}