diff options
author | sss <sss@dark-alexandr.net> | 2023-01-17 00:38:19 +0300 |
---|---|---|
committer | sss <sss@dark-alexandr.net> | 2023-01-17 00:38:19 +0300 |
commit | cc3f33db7a8d3c4ad373e646b199808e01bc5d9b (patch) | |
tree | ec09d690c7656ab5f2cc72607e05fb359c24d8b2 /src/rdp/rdp_impl.c |
added webrdp public code
Diffstat (limited to 'src/rdp/rdp_impl.c')
-rw-r--r-- | src/rdp/rdp_impl.c | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/src/rdp/rdp_impl.c b/src/rdp/rdp_impl.c new file mode 100644 index 0000000..3aa47e2 --- /dev/null +++ b/src/rdp/rdp_impl.c @@ -0,0 +1,637 @@ +/* BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>. + * + */ + +#include <errno.h> +#include <locale.h> +#include <pthread.h> +#include <stdlib.h> +#include <sys/select.h> + +#include <freerdp/client/channels.h> +#include <freerdp/client/cliprdr.h> +#include <freerdp/client/cmdline.h> +#include <freerdp/client/file.h> +#include <freerdp/constants.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> +#include <winpr/crt.h> +#include <winpr/synch.h> +#include <freerdp/log.h> + +#include <webrdp_core_api.h> +#include <webrdp_module_api.h> + +#include "rdp_backend_api.h" +#include "rdp_impl.h" +#include "rdp_display_output.h" +#include "rdp_channels.h" +#include "rdp_clipboard.h" +#include "rdp_ft.h" +#include "rdp_user_input.h" + +/* from winpr */ + +typedef BOOL (*pcIsHandled)(HANDLE handle); +typedef BOOL (*pcCloseHandle)(HANDLE handle); +typedef int (*pcGetFd)(HANDLE handle); +typedef DWORD (*pcCleanupHandle)(HANDLE handle); +typedef BOOL (*pcReadFile)(PVOID Object, LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcReadFileEx)(HANDLE hFile, LPVOID lpBuffer, + DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +typedef BOOL (*pcReadFileScatter)(HANDLE hFile, + FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead, + LPDWORD lpReserved, LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcWriteFile)(PVOID Object, LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcWriteFileEx)(HANDLE hFile, LPCVOID lpBuffer, + DWORD nNumberOfBytesToWrite, LPOVERLAPPED lpOverlapped, + LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); +typedef BOOL (*pcWriteFileGather)(HANDLE hFile, + FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToWrite, + LPDWORD lpReserved, LPOVERLAPPED lpOverlapped); +typedef DWORD (*pcGetFileSize)(HANDLE handle, LPDWORD lpFileSizeHigh); +typedef BOOL (*pcFlushFileBuffers)(HANDLE hFile); +typedef BOOL (*pcSetEndOfFile)(HANDLE handle); +typedef DWORD (*pcSetFilePointer)(HANDLE handle, LONG lDistanceToMove, + PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); +typedef BOOL (*pcSetFilePointerEx)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, + PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod); +typedef BOOL (*pcLockFile)(HANDLE hFile, DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh); +typedef BOOL (*pcLockFileEx)(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, + DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcUnlockFile)(HANDLE hFile, DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh); +typedef BOOL (*pcUnlockFileEx)(HANDLE hFile, DWORD dwReserved, + DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh, + LPOVERLAPPED lpOverlapped); +typedef BOOL (*pcSetFileTime)(HANDLE hFile, const FILETIME *lpCreationTime, + const FILETIME *lpLastAccessTime, const FILETIME *lpLastWriteTime); + +typedef struct _HANDLE_OPS +{ + pcIsHandled IsHandled; + pcCloseHandle CloseHandle; + pcGetFd GetFd; + pcCleanupHandle CleanupHandle; + pcReadFile ReadFile; + pcReadFileEx ReadFileEx; + pcReadFileScatter ReadFileScatter; + pcWriteFile WriteFile; + pcWriteFileEx WriteFileEx; + pcWriteFileGather WriteFileGather; + pcGetFileSize GetFileSize; + pcFlushFileBuffers FlushFileBuffers; + pcSetEndOfFile SetEndOfFile; + pcSetFilePointer SetFilePointer; + pcSetFilePointerEx SetFilePointerEx; + pcLockFile LockFile; + pcLockFileEx LockFileEx; + pcUnlockFile UnlockFile; + pcUnlockFileEx UnlockFileEx; + pcSetFileTime SetFileTime; +} HANDLE_OPS; + +#define WINPR_HANDLE_DEF() \ + ULONG Type; \ + ULONG Mode; \ + HANDLE_OPS *ops + +struct winpr_handle +{ + WINPR_HANDLE_DEF(); +}; +typedef struct winpr_handle WINPR_HANDLE; + +static INLINE BOOL +winpr_Handle_GetInfo(HANDLE handle, ULONG *pType, WINPR_HANDLE **pObject) +{ + WINPR_HANDLE *wHandle; + + if (handle == NULL || handle == INVALID_HANDLE_VALUE) + return FALSE; + + wHandle = (WINPR_HANDLE *)handle; + + *pType = wHandle->Type; + *pObject = handle; + + return TRUE; +} + +static INLINE int +winpr_Handle_getFd(HANDLE handle) +{ + WINPR_HANDLE *hdl; + ULONG type; + + if (!winpr_Handle_GetInfo(handle, &type, &hdl)) + return -1; + + if (!hdl || !hdl->ops || !hdl->ops->GetFd) + return -1; + + return hdl->ops->GetFd(handle); +} + +/* end from winpr */ + +bool +rdp_init_internals(void *internals) +{ + setlocale(LC_ALL, ""); +#ifdef DEBUG + setenv("WLOG_LEVEL", "DEBUG", 1); +#else + setenv("WLOG_LEVEL", "ERROR", 1); +#endif + rdp_internals *i = internals; + i->instance = freerdp_new(); + if (!i->instance) + { + const char *msg + = "rdp_module: Failed to create FreeRDP instance"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + goto error; + } + rdp_register_base(i->instance); + i->instance->ContextSize = sizeof(my_rdp_context); + if (!freerdp_context_new(i->instance)) + { + const char *msg + = "rdp_module: Failed to create FreeRDP context"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + goto error; + } + i->context = (my_rdp_context *)i->instance->context; + ((my_rdp_context *)i->instance->context)->my_internals = internals; + { + char **argv = malloc(sizeof(char *)); + if (!argv) + { + perror("malloc"); + goto error; + } + argv[0] = strdup("./core"); + rdpSettings *settings = i->instance->context->settings; + freerdp_client_settings_parse_command_line( + settings, 1, argv, FALSE); + free(argv[0]); + free(argv); + if (!settings) + { + const char *msg + = "rdp_module: Failed to create FreeRDP settings"; + i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_error, 0); + goto error; + } + settings->ThreadingFlags = THREADING_FLAGS_DISABLE_THREADS; + + settings->OsMajorType = OSMAJORTYPE_UNIX; + settings->OsMinorType = OSMINORTYPE_UNSPECIFIED; + + /* unsupported */ + settings->RemoteFxCodec = FALSE; + settings->NSCodec = FALSE; + + /* turn on clipboard redirect */ + settings->RedirectClipboard = TRUE; + } + i->context->ft_to_server = calloc(1, sizeof(my_rdp_ft)); + if (!i->context->ft_to_server) + { + perror("calloc"); + goto error; + } + i->context->ft_to_client = calloc(1, sizeof(my_rdp_ft)); + if (!i->context->ft_to_client) + { + perror("calloc"); + goto error; + } + freerdp_register_addin_provider( + freerdp_channels_load_static_addin_entry, 0); + i->settings = i->instance->context->settings; + //i->instance->settings = i->settings; + i->settings->instance = i->instance; + i->instance->context->settings = i->settings; + return true; +error: + if (i->instance) + { + if (i->instance->context) + { + freerdp_context_free(i->instance); + } + freerdp_free(i->instance); + i->instance = 0; + } + if (i->context->ft_to_client) + { + free(i->context->ft_to_client); + } + if (i->context->ft_to_server) + { + free(i->context->ft_to_server); + } + { + const char *msg = "rdp_module: internal error"; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + } + i->core->api_msgs->send_error_msg( + "rdp_module_internal_error", i->task_info); + return false; +} + +static void +rdp_check_all_fds(struct ev_loop *loop, ev_io *w) +{ + rdp_internals *i = w->data; + bool failure = false; + if (i->backend->stopped) + return; + if ((freerdp_check_event_handles(i->instance->context)) != TRUE) + { + uint32_t e; + e = freerdp_error_info(i->instance); + switch (e) + { + case 1: + case 2: + case 7: + case 9: + /* case 12: */ + /* No really an error + * (Happens when you invoke Disconnect + * in Start-Menu) */ + break; + case 5: + { + const char *msg + = "rdp_module: Another user connected to " + "the server,\nforcing the " + "disconnection of the " + "current connection."; + i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_debug, 0); + break; + } + default: + { + failure = true; + } + break; + } + if (failure) + { + char buf[512]; + snprintf(buf, 511, "%s", + freerdp_get_last_error_name( + freerdp_get_last_error(i->instance->context))); + i->core->api_msgs->send_error_msg(buf, i->task_info); + i->core->api_utils->log_msg((const uint8_t *)buf, + strlen(buf), wrdp_log_level_error, 0); + goto cleanup; + } + } + if (freerdp_shall_disconnect_context(i->instance->context)) + { + { + const char *msg = "rdp_module: disconnect, no error"; + i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_trace, 0); + } + freerdp_disconnect(i->instance); + i->conn_state = rdp_conn_state_offline; + i->core->api_msgs->send_error_msg( + "rdp_module_disconnect_no_error", i->task_info); + goto cleanup; + } + return; +cleanup: + ev_io_stop(loop, w); + if (!failure) + { + i->core->api_msgs->send_termination_msg(i->task_info); + } + i->core->api_core->task_finished(true, i->task_info); +} + +static void +rdp_fd_readable_cb(struct ev_loop *loop, ev_io *w, int revents) +{ + rdp_check_all_fds(loop, w); +} + +bool +rdp_connect(void *internals) +{ + rdp_internals *_i = internals; +#ifdef DEBUG + { + /* print settings before connect for debug */ + char buf[512]; + snprintf(buf, 511, + "rdp_module: settings:\n\tfntlm: %ld\n\t" + "height: %ld\n\thost: %s\n\tnomani: %ld\n\tnonla: %ld\n\t" + "notheme: %ld\n\tnotls: %ld\n\tnowallp: %ld\n\tnowdrag: " + "%ld\n\t" + "password: %s\n\tpcb: %s\n\tperf: %ld\n\tport: " + "%ld\n\tuser: %s\n\t" + "width: %ld", + _i->my_settings.fntlm.value, _i->my_settings.height.value, + _i->my_settings.host.value, _i->my_settings.nomani.value, + _i->my_settings.nonla.value, _i->my_settings.notheme.value, + _i->my_settings.notls.value, _i->my_settings.nowallp.value, + _i->my_settings.nowdrag.value, + _i->my_settings.password.value, _i->my_settings.pcb.value, + _i->my_settings.perf.value, _i->my_settings.port.value, + _i->my_settings.user.value, _i->my_settings.width.value); + _i->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), wrdp_log_level_trace, 0); + } +#endif /* DEBUG */ + if (!_i->my_settings.host.value || !_i->my_settings.host.value[0]) + { + const char *msg = "rdp_module: host is not set"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_param_error", _i->task_info); + return false; + } + if (!_i->my_settings.port.value) + { + const char *msg = "rdp_module: port is not set"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_param_error", _i->task_info); + return false; + } + if (!_i->my_settings.user.value || !_i->my_settings.user.value[0]) + { + const char *msg = "rdp_module: user is not set"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_param_error", _i->task_info); + return false; + } + /* password can be optional ? */ + if (freerdp_connect(_i->instance)) + { + int fdcount = 0, i; + HANDLE *fds[256]; + ZeroMemory(fds, sizeof(fds)); + + struct ev_loop *loop + = _i->core->api_core->get_libev_loop(_i->task_info); + fdcount = freerdp_get_event_handles( + _i->instance->context, (HANDLE *)fds, 256); + if (!fdcount) + { + const char *msg + = "Failed to get FreeRDP file descriptors"; + _i->core->api_utils->log_msg((const uint8_t *)msg, + strlen(msg), wrdp_log_level_error, 0); + _i->core->api_msgs->send_error_msg( + "rdp_module_internal_error", _i->task_info); + return false; + } + _i->rdp_fd_count = fdcount; + for (i = 0; i < fdcount; ++i) + { + ULONG type; + WINPR_HANDLE *phandle = 0; + int fd = 0; + if (!winpr_Handle_GetInfo(fds[i], &type, &phandle)) + { + /* TODO: assume what this is not fatal */ + const char *msg = "winpr_Handle_GetInfo failed"; + _i->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), + wrdp_log_level_error, 0); + continue; + } + fd = winpr_Handle_getFd(phandle); + + /* TODO: check type here, and set EV_WRITE if needed + * for now watch only for read available event*/ + ev_io_init( + &(_i->rdp_fd[i]), rdp_fd_readable_cb, fd, EV_READ); + _i->rdp_fd[i].data = internals; + ev_io_start(loop, &(_i->rdp_fd[i])); + } + return true; + } + { + const char *msg = freerdp_get_last_error_name( + freerdp_get_last_error(_i->instance->context)); + _i->core->api_msgs->send_error_msg(msg, _i->task_info); + } + + return false; +} + +static BOOL +rdp_pre_connect(freerdp *instance) +{ + rdpSettings *settings = instance->context->settings; + my_rdp_context *c = (my_rdp_context *)instance->context; + + RegisterUpdate(instance); + RegisterPrimary(instance); + + /* looks like this settings reducing artifacts level */ + settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_SCRBLT_INDEX] = FALSE; + + /* causing random disconnects */ + settings->OrderSupport[NEG_ATEXTOUT_INDEX] = FALSE; + settings->OrderSupport[NEG_DRAWNINEGRID_INDEX] = FALSE; + settings->NoBitmapCompressionHeader = TRUE; + settings->BitmapCompressionDisabled = TRUE; + settings->BitmapCacheEnabled = FALSE; + settings->BrushSupportLevel = 0; + + /* causing connection failure on recent versions of freerdp */ + settings->OffscreenSupportLevel = 0; + settings->OffscreenCacheSize = 0; + + /* following required at least on windows 7 */ + settings->OrderSupport[NEG_MEMBLT_INDEX] = FALSE; + settings->OrderSupport[NEG_MEM3BLT_INDEX] = FALSE; + + /* following required for windows 2012,2016 */ + settings->FastPathOutput = 1; + + /* testing */ + /* wsgate settings */ + // settings->ColorDepth = 16; + // settings->FrameAcknowledge = 1; + // settings->LargePointerFlag = 1; + // settings->BitmapCacheV3Enabled = FALSE; + // settings->BitmapCachePersistEnabled = FALSE; + // settings->OrderSupport[NEG_DSTBLT_INDEX] = TRUE; + // settings->OrderSupport[NEG_PATBLT_INDEX] = FALSE; // XXX + // settings->OrderSupport[NEG_SCRBLT_INDEX] = FALSE; // XXX + // settings->OrderSupport[NEG_OPAQUE_RECT_INDEX] = TRUE; + // settings->OrderSupport[NEG_MULTIDSTBLT_INDEX] = FALSE; + // settings->OrderSupport[NEG_MULTIPATBLT_INDEX] = FALSE; + // settings->OrderSupport[NEG_MULTISCRBLT_INDEX] = FALSE; + // settings->OrderSupport[NEG_MULTIOPAQUERECT_INDEX] = FALSE; // + //XXX settings->OrderSupport[NEG_MULTI_DRAWNINEGRID_INDEX] = FALSE; + // settings->OrderSupport[NEG_LINETO_INDEX] = TRUE; + // settings->OrderSupport[NEG_POLYLINE_INDEX] = TRUE; + // settings->OrderSupport[NEG_MEMBLT_V2_INDEX] = FALSE; + // settings->OrderSupport[NEG_MEM3BLT_V2_INDEX] = FALSE; + // settings->OrderSupport[NEG_SAVEBITMAP_INDEX] = FALSE; + // settings->OrderSupport[NEG_GLYPH_INDEX_INDEX] = TRUE; + // settings->OrderSupport[NEG_FAST_INDEX_INDEX] = TRUE; + // settings->OrderSupport[NEG_FAST_GLYPH_INDEX] = TRUE; + // settings->OrderSupport[NEG_POLYGON_SC_INDEX] = FALSE; + // settings->OrderSupport[NEG_POLYGON_CB_INDEX] = FALSE; + // settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; + // settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; + // settings->GlyphSupportLevel = GLYPH_SUPPORT_NONE; + + PubSub_SubscribeChannelConnected( + instance->context->pubSub, rdp_on_channel_connected); + PubSub_SubscribeChannelDisconnected( + instance->context->pubSub, rdp_on_channel_disconnected); + + freerdp_client_load_addins(instance->context->channels, settings); + instance->context->cache = cache_new(instance->context); + + c->my_internals->clrconv + = freerdp_clrconv_new(CLRCONV_ALPHA | CLRCONV_INVERT); + + if (!instance->context->cache) + { + return FALSE; + } + return TRUE; +} + +static BOOL +rdp_post_connect(freerdp *instance) +{ + my_rdp_context *c = (my_rdp_context *)instance->context; + my_rdp_clipboard *clip = 0; + rdpPointer p; + memset(&p, 0, sizeof(p)); + p.size = sizeof(my_rdp_pointer); + register_pointer(&p); + graphics_register_pointer(instance->context->graphics, &p); + pointer_cache_register_callbacks(instance->context->update); + + clip = rdp_clipboard_new(c); + if (!clip) + { + const char *msg = "rdp_module: \"rdp_clipboard_new\" failed"; + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_error, 0); + return FALSE; + } + c->clipboard = clip; + + c->my_internals->conn_state = rdp_conn_state_connected; + + { + const char *msg + = "rdp_module: \"C:RDP session connection started.\""; + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)msg, strlen(msg), wrdp_log_level_trace, 0); + } + + c->my_internals->core->api_msgs->send_text_msg( + "C:RDP session connection started.", c->my_internals->task_info); + + instance->context->update->DesktopResize(instance->context); + + return TRUE; +} + +static DWORD +rdp_verify_certificate(freerdp *instance, const char *host, UINT16 port, + const char *common_name, const char *subject, const char *issuer, + const char *fingerprint, DWORD flags) +{ + my_rdp_context *c = (my_rdp_context *)instance->context; + char buf[512]; + snprintf(buf, 511, + "Certificate details:\n\tSubject: %s\n\tIssuer:" + " %s\n\tThumbprint: %s\n" + "The above X.509 certificate could not be verified, possibly " + "because you do not have " + "the CA certificate in your certificate store, or the " + "certificate has expired. " + "Please look at the OpenSSL documentation on how to add a " + "private CA to the store.", + subject, issuer, fingerprint); + c->my_internals->core->api_utils->log_msg( + (const uint8_t *)buf, strlen(buf), wrdp_log_level_debug, 0); + + return TRUE; /* ?? */ +} + +/*static int +rdp_receive_channel_data (freerdp* instance, + UINT16 channelId, + BYTE* data, + int size, + int flags, + int total_size) +{ + return freerdp_channels_data ( + instance, channelId, data, size, flags, total_size); +}*/ + +static BOOL +rdp_context_new(freerdp *instance, rdpContext *context) +{ + return TRUE; +} + +static void +rdp_context_free(freerdp *instance, rdpContext *context) +{ + my_rdp_context *c = (my_rdp_context *)context; + if (context->cache) + { + cache_free(context->cache); + context->cache = NULL; + } + if (c->my_internals->clrconv) + { + freerdp_clrconv_free(c->my_internals->clrconv); + c->my_internals->clrconv = NULL; + } +} + +void +rdp_register_base(freerdp *r) +{ + r->PreConnect = rdp_pre_connect; + r->PostConnect = rdp_post_connect; + r->VerifyCertificateEx = rdp_verify_certificate; + // r->ReceiveChannelData = rdp_receive_channel_data; + r->ContextNew = rdp_context_new; + r->ContextFree = rdp_context_free; +} |