Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,31 @@
if(CMAKE_CROSSCOMPILING)
find_package(GenerateArgumentManpage)
else()
include_directories(${CMAKE_BINARY_DIR}/include/)
add_executable(generate_argument_manpage generate_argument_manpage.c ../cmdline.h)
include(CompilerDetect)
if(CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_GNUCC)
target_compile_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
target_compile_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
target_link_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
target_link_options(generate_argument_manpage PRIVATE -fno-sanitize=all)
endif()
export(TARGETS generate_argument_manpage FILE "${CMAKE_BINARY_DIR}/GenerateArgumentManpageConfig.cmake")
endif()
set(MAN_OPTIONS_FILE "${CMAKE_CURRENT_BINARY_DIR}/freerdp-global-options.1")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${MAN_OPTIONS_FILE})
add_custom_command(
OUTPUT ${MAN_OPTIONS_FILE} COMMAND $<TARGET_FILE:generate_argument_manpage> "${MAN_OPTIONS_FILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS generate_argument_manpage
)
add_custom_target(
generate_argument_manpage.target DEPENDS ${MAN_OPTIONS_FILE} ${CMAKE_BINARY_DIR}/include/freerdp/config.h
$<TARGET_FILE:generate_argument_manpage>
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)

View File

@@ -0,0 +1,82 @@
.SH "GLOBAL CONFIGURATION (client common)"
.PP
Format and Location:
.RS 4
The configuration files are stored in global system configuration\&.
.br
The common certificate settings location is
\fI@SYSCONF_DIR@/certificates\&.json\fR
.br
File format is JSON
.RE
.PP
Supported options:
.RS 4
.PP
\fIdeny\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
Deny the certificate if the check against system SSL store was not successful
.RE
.RE
.PP
\fIignore\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
Ignore certificate failures, just ignore the certificate
.RE
.RE
.PP
\fIdeny\-userconfig\fR
.RS 4
.PP
.RS 4
\fIJSON boolean\fR
.br
If the checks in the global configuration do not accept the certificate do not ask the user
.RE
.RE
.PP
\fIcertificate\-db\fR
.RS 4
.PP
\fIJSON array\fR
.RS 4
An array of
\fIJSON objects\fR
with:
.PP
\fItype\fR
.RS 4
\fIJSON string\fR
.br
a string identifying the hash algorithm used, e\&.g\&.
\fIsha256\fR
.br
.RE
.PP
\fIhash\fR
.RS 4
\fIJSON string\fR
.br
a string of hex integer values representing the certificate hash, e\&.g\&.
\fI0123456789abcdef\fR
.RE
.RE
.RE
.RE

View File

@@ -0,0 +1,17 @@
.SH "ENVIRONMENT VARIABLES"
.PP
wlog environment variables
.RS 4
@MANPAGE_NAME@ uses wLog as its log facility, you can refer to the corresponding man page (wlog(7)) for more information\&. Arguments passed via the
\fI/log\-level\fR
or
\fI/log\-filters\fR
have precedence over the environment variables\&.
.br
.RE
.PP
FREERDP_ASKPASS
.RS 4
\fISet a program to ask for passwords similar to SSH_ASKPASS\fR
.RE

View File

@@ -0,0 +1,8 @@
.SH "LINKS"
.PP
\m[blue]\fBhttp://www\&.freerdp\&.com/\fR\m[]
.SH "AUTHOR"
.br
.PP
The FreeRDP Team

View File

@@ -0,0 +1,262 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "../cmdline.h"
static char* resize(char** buffer, size_t* size, size_t increment)
{
const size_t nsize = *size + increment;
char* tmp = realloc(*buffer, nsize);
if (!tmp)
{
(void)fprintf(stderr,
"Could not reallocate string buffer from %" PRIuz " to %" PRIuz " bytes.\n",
*size, nsize);
free(*buffer);
return nullptr;
}
memset(&tmp[*size], '\0', increment);
*size = nsize;
*buffer = tmp;
return tmp;
}
static char* append(char** buffer, size_t* size, const char* str)
{
const size_t len = strnlen(*buffer, *size);
const size_t add = strlen(str);
const size_t required = len + add + 1;
if (required > *size)
{
if (!resize(buffer, size, required - *size))
return nullptr;
}
strncpy(&(*buffer)[len], str, add);
return *buffer;
}
static LPSTR tr_esc_str(LPCSTR arg, bool format, int* failed)
{
const char* str = nullptr;
LPSTR tmp = nullptr;
size_t ds = 0;
assert(failed);
if (nullptr == arg)
return nullptr;
const size_t s = strlen(arg) + 1;
if (!resize(&tmp, &ds, s))
{
*failed = -2;
return nullptr;
}
for (size_t x = 0; x < s; x++)
{
char data[2] = WINPR_C_ARRAY_INIT;
switch (arg[x])
{
case '-':
str = "\\-";
if (!append(&tmp, &ds, str))
{
*failed = -3;
return nullptr;
}
break;
case '<':
if (format)
str = "\\fI";
else
str = "<";
if (!append(&tmp, &ds, str))
{
*failed = -4;
return nullptr;
}
break;
case '>':
if (format)
str = "\\fR";
else
str = ">";
if (!append(&tmp, &ds, str))
{
*failed = -5;
return nullptr;
}
break;
case '\'':
str = "\\*(Aq";
if (!append(&tmp, &ds, str))
{
*failed = -6;
return nullptr;
}
break;
case '.':
if (!append(&tmp, &ds, "\\&."))
{
*failed = -7;
return nullptr;
}
break;
case '\r':
case '\n':
if (!append(&tmp, &ds, "\n.br\n"))
{
*failed = -8;
return nullptr;
}
break;
default:
data[0] = arg[x];
if (!append(&tmp, &ds, data))
{
*failed = -9;
return nullptr;
}
break;
}
}
return tmp;
}
int main(int argc, char* argv[])
{
int rc = -3;
size_t elements = sizeof(global_cmd_args) / sizeof(global_cmd_args[0]);
if (argc != 2)
{
(void)fprintf(stderr, "Usage: %s <output file name>\n", argv[0]);
return -1;
}
const char* fname = argv[1];
(void)fprintf(stdout, "Generating manpage file '%s'\n", fname);
FILE* fp = fopen(fname, "w");
if (nullptr == fp)
{
(void)fprintf(stderr, "Could not open '%s' for writing.\n", fname);
return -1;
}
/* The tag used as header in the manpage */
(void)fprintf(fp, ".SH \"OPTIONS\"\n");
if (elements < 2)
{
(void)fprintf(stderr, "The argument array 'args' is empty, writing an empty file.\n");
elements = 1;
}
for (size_t x = 0; x < elements - 1; x++)
{
int failed = 0;
const COMMAND_LINE_ARGUMENT_A* arg = &global_cmd_args[x];
char* name = tr_esc_str(arg->Name, FALSE, &failed);
char* alias = tr_esc_str(arg->Alias, FALSE, &failed);
char* format = tr_esc_str(arg->Format, TRUE, &failed);
char* text = tr_esc_str(arg->Text, FALSE, &failed);
if (failed != 0)
{
free(name);
free(alias);
free(format);
free(text);
rc = failed;
goto fail;
}
(void)fprintf(fp, ".PP\n");
bool first = true;
do
{
(void)fprintf(fp, "%s\\fB", first ? "" : ", ");
first = false;
if (arg->Flags == COMMAND_LINE_VALUE_BOOL)
(void)fprintf(fp, "%s", arg->Default ? "\\-" : "+");
else
(void)fprintf(fp, "/");
(void)fprintf(fp, "%s\\fR", name);
if (format)
{
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
(void)fprintf(fp, "[");
(void)fprintf(fp, ":%s", format);
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
(void)fprintf(fp, "]");
}
if (alias == name)
break;
free(name);
name = alias;
} while (alias);
(void)fprintf(fp, "\n");
if (text)
{
(void)fprintf(fp, ".RS 4\n");
const int hasText = text && (strnlen(text, 2) > 0);
if (hasText)
(void)fprintf(fp, "%s", text);
if (arg->Flags & COMMAND_LINE_VALUE_BOOL &&
(!arg->Default || arg->Default == BoolValueTrue))
(void)fprintf(fp, " (default:%s)\n", arg->Default ? "on" : "off");
else if (arg->Default)
{
char* value = tr_esc_str(arg->Default, FALSE, &failed);
if (failed != 0)
{
rc = failed;
goto fail;
}
(void)fprintf(fp, " (default:%s)\n", value);
free(value);
}
else if (hasText)
(void)fprintf(fp, "\n");
}
(void)fprintf(fp, ".RE\n");
free(name);
free(format);
free(text);
}
rc = 0;
fail:
(void)fclose(fp);
if (rc == 0)
(void)fprintf(stdout, "successfully generated '%s'\n", fname);
else
(void)fprintf(stdout, "failed to generate '%s'\n", fname);
return rc;
}