Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
493
third_party/FreeRDP/libfreerdp/utils/passphrase.c
vendored
Normal file
493
third_party/FreeRDP/libfreerdp/utils/passphrase.c
vendored
Normal file
@@ -0,0 +1,493 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Passphrase Handling Utils
|
||||
*
|
||||
* Copyright 2011 Shea Levy <shea@shealevy.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/environment.h>
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <freerdp/utils/passphrase.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <conio.h>
|
||||
#include <wincred.h>
|
||||
|
||||
static char read_chr(FILE* f)
|
||||
{
|
||||
char chr;
|
||||
const BOOL isTty = _isatty(_fileno(f));
|
||||
if (isTty)
|
||||
return fgetc(f);
|
||||
if (fscanf_s(f, "%c", &chr, (UINT32)sizeof(char)) && !feof(f))
|
||||
return chr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int freerdp_interruptible_getc(rdpContext* context, FILE* f)
|
||||
{
|
||||
return read_chr(f);
|
||||
}
|
||||
|
||||
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
|
||||
size_t bufsiz, int from_stdin)
|
||||
{
|
||||
WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 'p', 'r', 'e', 'f', 'i',
|
||||
'l', 'l', 'e', 'd', '\0' };
|
||||
WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = WINPR_C_ARRAY_INIT;
|
||||
BOOL fSave = FALSE;
|
||||
DWORD dwFlags = 0;
|
||||
WCHAR* promptW = ConvertUtf8ToWCharAlloc(prompt, nullptr);
|
||||
const DWORD status =
|
||||
CredUICmdLinePromptForCredentialsW(promptW, nullptr, 0, UserNameW, ARRAYSIZE(UserNameW),
|
||||
PasswordW, ARRAYSIZE(PasswordW), &fSave, dwFlags);
|
||||
free(promptW);
|
||||
if (ConvertWCharNToUtf8(PasswordW, ARRAYSIZE(PasswordW), buf, bufsiz) < 0)
|
||||
return nullptr;
|
||||
return buf;
|
||||
}
|
||||
|
||||
#elif !defined(ANDROID)
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <freerdp/utils/signal.h>
|
||||
#include <freerdp/log.h>
|
||||
#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
|
||||
#include <poll.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#define TAG FREERDP_TAG("utils.passphrase")
|
||||
|
||||
static int wait_for_fd(int fd, int timeout)
|
||||
{
|
||||
int status = 0;
|
||||
#if defined(WINPR_HAVE_POLL_H) && !defined(__APPLE__)
|
||||
struct pollfd pollset = WINPR_C_ARRAY_INIT;
|
||||
pollset.fd = fd;
|
||||
pollset.events = POLLIN;
|
||||
pollset.revents = 0;
|
||||
|
||||
do
|
||||
{
|
||||
status = poll(&pollset, 1, timeout);
|
||||
} while ((status < 0) && (errno == EINTR));
|
||||
|
||||
#else
|
||||
fd_set rset = WINPR_C_ARRAY_INIT;
|
||||
struct timeval tv = WINPR_C_ARRAY_INIT;
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(fd, &rset);
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout % 1000) * 1000;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
status = select(fd + 1, &rset, nullptr, nullptr, timeout ? &tv : nullptr);
|
||||
} while ((status < 0) && (errno == EINTR));
|
||||
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
static void replace_char(char* buffer, WINPR_ATTR_UNUSED size_t buffer_len, const char* toreplace)
|
||||
{
|
||||
while (*toreplace != '\0')
|
||||
{
|
||||
char* ptr = nullptr;
|
||||
while ((ptr = strrchr(buffer, *toreplace)) != nullptr)
|
||||
*ptr = '\0';
|
||||
toreplace++;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* freerdp_passphrase_read_tty(rdpContext* context, const char* prompt, char* buf,
|
||||
size_t bufsiz, int from_stdin)
|
||||
{
|
||||
BOOL terminal_needs_reset = FALSE;
|
||||
char term_name[L_ctermid] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
FILE* fout = nullptr;
|
||||
|
||||
if (bufsiz == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ctermid(term_name);
|
||||
int terminal_fildes = 0;
|
||||
if (from_stdin || (strcmp(term_name, "") == 0))
|
||||
{
|
||||
fout = stdout;
|
||||
terminal_fildes = STDIN_FILENO;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int term_file = open(term_name, O_RDWR);
|
||||
if (term_file < 0)
|
||||
{
|
||||
fout = stdout;
|
||||
terminal_fildes = STDIN_FILENO;
|
||||
}
|
||||
else
|
||||
{
|
||||
fout = fdopen(term_file, "w");
|
||||
if (!fout)
|
||||
{
|
||||
close(term_file);
|
||||
return nullptr;
|
||||
}
|
||||
terminal_fildes = term_file;
|
||||
}
|
||||
}
|
||||
|
||||
struct termios orig_flags = WINPR_C_ARRAY_INIT;
|
||||
if (tcgetattr(terminal_fildes, &orig_flags) != -1)
|
||||
{
|
||||
struct termios new_flags = WINPR_C_ARRAY_INIT;
|
||||
new_flags = orig_flags;
|
||||
new_flags.c_lflag &= (uint32_t)~ECHO;
|
||||
new_flags.c_lflag |= ECHONL;
|
||||
terminal_needs_reset = TRUE;
|
||||
if (tcsetattr(terminal_fildes, TCSAFLUSH, &new_flags) == -1)
|
||||
terminal_needs_reset = FALSE;
|
||||
}
|
||||
|
||||
FILE* fp = fdopen(terminal_fildes, "r");
|
||||
if (!fp)
|
||||
goto error;
|
||||
|
||||
(void)fprintf(fout, "%s", prompt);
|
||||
(void)fflush(fout);
|
||||
|
||||
{
|
||||
char* ptr = nullptr;
|
||||
size_t ptr_len = 0;
|
||||
const SSIZE_T res = freerdp_interruptible_get_line(context, &ptr, &ptr_len, fp);
|
||||
if (res < 0)
|
||||
goto error;
|
||||
|
||||
replace_char(ptr, ptr_len, "\r\n");
|
||||
|
||||
strncpy(buf, ptr, MIN(bufsiz, ptr_len));
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
if (terminal_needs_reset)
|
||||
{
|
||||
if (tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags) == -1)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (terminal_fildes != STDIN_FILENO)
|
||||
(void)fclose(fp);
|
||||
|
||||
return buf;
|
||||
|
||||
error:
|
||||
{
|
||||
// NOLINTNEXTLINE(clang-analyzer-unix.Stream)
|
||||
int saved_errno = errno;
|
||||
if (terminal_needs_reset)
|
||||
(void)tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags);
|
||||
|
||||
if (terminal_fildes != STDIN_FILENO)
|
||||
{
|
||||
if (fp)
|
||||
(void)fclose(fp);
|
||||
}
|
||||
// NOLINTNEXTLINE(clang-analyzer-unix.Stream)
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char* freerdp_passphrase_read_askpass(const char* prompt, char* buf, size_t bufsiz,
|
||||
char const* askpass_env)
|
||||
{
|
||||
char command[4096] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
(void)sprintf_s(command, sizeof(command), "%s 'FreeRDP authentication\n%s'", askpass_env,
|
||||
prompt);
|
||||
// NOLINTNEXTLINE(clang-analyzer-optin.taint.GenericTaint)
|
||||
FILE* askproc = popen(command, "r");
|
||||
if (!askproc)
|
||||
return nullptr;
|
||||
WINPR_ASSERT(bufsiz <= INT32_MAX);
|
||||
if (fgets(buf, (int)bufsiz, askproc) != nullptr)
|
||||
buf[strcspn(buf, "\r\n")] = '\0';
|
||||
else
|
||||
buf = nullptr;
|
||||
const int status = pclose(askproc);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
buf = nullptr;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
|
||||
size_t bufsiz, int from_stdin)
|
||||
{
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
const char* askpass_env = getenv("FREERDP_ASKPASS");
|
||||
|
||||
if (askpass_env)
|
||||
return freerdp_passphrase_read_askpass(prompt, buf, bufsiz, askpass_env);
|
||||
else
|
||||
return freerdp_passphrase_read_tty(context, prompt, buf, bufsiz, from_stdin);
|
||||
}
|
||||
|
||||
static BOOL set_termianl_nonblock(int ifd, BOOL nonblock);
|
||||
|
||||
static void restore_terminal(void)
|
||||
{
|
||||
(void)set_termianl_nonblock(-1, FALSE);
|
||||
}
|
||||
|
||||
BOOL set_termianl_nonblock(int ifd, BOOL nonblock)
|
||||
{
|
||||
static int fd = -1;
|
||||
static bool registered = false;
|
||||
static int orig = 0;
|
||||
static struct termios termios = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (ifd >= 0)
|
||||
fd = ifd;
|
||||
|
||||
if (fd < 0)
|
||||
return FALSE;
|
||||
|
||||
if (nonblock)
|
||||
{
|
||||
if (!registered)
|
||||
{
|
||||
(void)atexit(restore_terminal);
|
||||
registered = true;
|
||||
}
|
||||
|
||||
const int rc1 = fcntl(fd, F_SETFL, orig | O_NONBLOCK);
|
||||
if (rc1 != 0)
|
||||
{
|
||||
char buffer[128] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
|
||||
winpr_strerror(errno, buffer, sizeof(buffer)));
|
||||
return FALSE;
|
||||
}
|
||||
const int rc2 = tcgetattr(fd, &termios);
|
||||
if (rc2 != 0)
|
||||
{
|
||||
char buffer[128] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "tcgetattr() failed with %s",
|
||||
winpr_strerror(errno, buffer, sizeof(buffer)));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct termios now = termios;
|
||||
cfmakeraw(&now);
|
||||
const int rc3 = tcsetattr(fd, TCSANOW, &now);
|
||||
if (rc3 != 0)
|
||||
{
|
||||
char buffer[128] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
|
||||
winpr_strerror(errno, buffer, sizeof(buffer)));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int rc1 = tcsetattr(fd, TCSANOW, &termios);
|
||||
if (rc1 != 0)
|
||||
{
|
||||
char buffer[128] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "tcsetattr(TCSANOW) failed with %s",
|
||||
winpr_strerror(errno, buffer, sizeof(buffer)));
|
||||
return FALSE;
|
||||
}
|
||||
const int rc2 = fcntl(fd, F_SETFL, orig);
|
||||
if (rc2 != 0)
|
||||
{
|
||||
char buffer[128] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "fcntl(F_SETFL) failed with %s",
|
||||
winpr_strerror(errno, buffer, sizeof(buffer)));
|
||||
return FALSE;
|
||||
}
|
||||
fd = -1;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int freerdp_interruptible_getc(rdpContext* context, FILE* stream)
|
||||
{
|
||||
int rc = EOF;
|
||||
const int fd = fileno(stream);
|
||||
|
||||
(void)set_termianl_nonblock(fd, TRUE);
|
||||
|
||||
do
|
||||
{
|
||||
const int res = wait_for_fd(fd, 10);
|
||||
if (res != 0)
|
||||
{
|
||||
char c = 0;
|
||||
const ssize_t rd = read(fd, &c, 1);
|
||||
if (rd == 1)
|
||||
{
|
||||
if (c == 3) /* ctrl + c */
|
||||
return EOF;
|
||||
if (c == 4) /* ctrl + d */
|
||||
return EOF;
|
||||
if (c == 26) /* ctrl + z */
|
||||
return EOF;
|
||||
rc = (int)c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (!freerdp_shall_disconnect_context(context));
|
||||
|
||||
(void)set_termianl_nonblock(fd, FALSE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const char* freerdp_passphrase_read(rdpContext* context, const char* prompt, char* buf,
|
||||
size_t bufsiz, int from_stdin)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int freerdp_interruptible_getc(rdpContext* context, FILE* f)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
#endif
|
||||
|
||||
SSIZE_T freerdp_interruptible_get_line(rdpContext* context, char** plineptr, size_t* psize,
|
||||
FILE* stream)
|
||||
{
|
||||
int c = 0;
|
||||
char* n = nullptr;
|
||||
size_t step = 32;
|
||||
size_t used = 0;
|
||||
char* ptr = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
if (!plineptr || !psize)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool echo = true;
|
||||
#if !defined(_WIN32) && !defined(ANDROID)
|
||||
{
|
||||
const int fd = fileno(stream);
|
||||
|
||||
struct termios termios = WINPR_C_ARRAY_INIT;
|
||||
/* This might fail if /from-stdin is used. */
|
||||
if (tcgetattr(fd, &termios) == 0)
|
||||
echo = (termios.c_lflag & ECHO) != 0;
|
||||
else
|
||||
echo = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (*plineptr && (*psize > 0))
|
||||
{
|
||||
ptr = *plineptr;
|
||||
used = *psize;
|
||||
if (echo)
|
||||
{
|
||||
printf("%s", ptr);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (used + 2 >= len)
|
||||
{
|
||||
len += step;
|
||||
n = realloc(ptr, len);
|
||||
|
||||
if (!n)
|
||||
{
|
||||
free(ptr);
|
||||
*plineptr = nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr = n;
|
||||
}
|
||||
|
||||
c = freerdp_interruptible_getc(context, stream);
|
||||
if (c == 127)
|
||||
{
|
||||
if (used > 0)
|
||||
{
|
||||
ptr[used--] = '\0';
|
||||
if (echo)
|
||||
{
|
||||
printf("\b");
|
||||
printf(" ");
|
||||
printf("\b");
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (echo)
|
||||
{
|
||||
printf("%c", c);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
if (c != EOF)
|
||||
ptr[used++] = (char)c;
|
||||
} while ((c != '\n') && (c != '\r') && (c != EOF));
|
||||
|
||||
printf("\n");
|
||||
ptr[used] = '\0';
|
||||
if (c == EOF)
|
||||
{
|
||||
free(ptr);
|
||||
*plineptr = nullptr;
|
||||
return EOF;
|
||||
}
|
||||
*plineptr = ptr;
|
||||
*psize = used;
|
||||
return WINPR_ASSERTING_INT_CAST(SSIZE_T, used);
|
||||
}
|
||||
Reference in New Issue
Block a user