/* BSD-2-Clause license * * Copyright (c) 2018-2023 NST , sss . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }