cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

rail.c (10012B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "channels/rail.h"
     21#include "plugins/channels.h"
     22#include "rdp.h"
     23#include "settings.h"
     24
     25#include <freerdp/client/rail.h>
     26#include <freerdp/event.h>
     27#include <freerdp/freerdp.h>
     28#include <freerdp/rail.h>
     29#include <guacamole/client.h>
     30#include <winpr/wtypes.h>
     31#include <winpr/wtsapi.h>
     32
     33#include <stddef.h>
     34#include <string.h>
     35
     36#ifdef FREERDP_RAIL_CALLBACKS_REQUIRE_CONST
     37/**
     38 * FreeRDP 2.0.0-rc4 and newer requires the final argument for all RAIL
     39 * callbacks to be const.
     40 */
     41#define RAIL_CONST const
     42#else
     43/**
     44 * FreeRDP 2.0.0-rc3 and older requires the final argument for all RAIL
     45 * callbacks to NOT be const.
     46 */
     47#define RAIL_CONST
     48#endif
     49
     50/**
     51 * Completes initialization of the RemoteApp session, responding to the server
     52 * handshake, sending client status and system parameters, and executing the
     53 * desired RemoteApp command. This is accomplished using the Handshake PDU,
     54 * Client Information PDU, one or more Client System Parameters Update PDUs,
     55 * and the Client Execute PDU respectively. These PDUs MUST be sent for the
     56 * desired RemoteApp to run, and MUST NOT be sent until after a Handshake or
     57 * HandshakeEx PDU has been received. See:
     58 *
     59 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/cec4eb83-b304-43c9-8378-b5b8f5e7082a (Handshake PDU)
     60 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/743e782d-f59b-40b5-a0f3-adc74e68a2ff (Client Information PDU)
     61 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/60344497-883f-4711-8b9a-828d1c580195 (System Parameters Update PDU)
     62 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/98a6e3c3-c2a9-42cc-ad91-0d9a6c211138 (Client Execute PDU)
     63 *
     64 * @param rail
     65 *     The RailClientContext structure used by FreeRDP to handle the RAIL
     66 *     channel for the current RDP session.
     67 *
     68 * @return
     69 *     CHANNEL_RC_OK (zero) if the PDUs were sent successfully, an error code
     70 *     (non-zero) otherwise.
     71 */
     72static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) {
     73
     74    UINT status;
     75
     76    guac_client* client = (guac_client*) rail->custom;
     77    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
     78
     79    RAIL_HANDSHAKE_ORDER handshake = {
     80
     81        /* Build number 7600 (0x1DB0) apparently represents Windows 7 and
     82         * compatibility with RDP 7.0. As of this writing, this is the same
     83         * build number sent for RAIL connections by xfreerdp. */
     84        .buildNumber = 7600
     85
     86    };
     87
     88    /* Send client handshake response */
     89    pthread_mutex_lock(&(rdp_client->message_lock));
     90    status = rail->ClientHandshake(rail, &handshake);
     91    pthread_mutex_unlock(&(rdp_client->message_lock));
     92
     93    if (status != CHANNEL_RC_OK)
     94        return status;
     95
     96    RAIL_CLIENT_STATUS_ORDER client_status = {
     97        .flags = 0x00
     98    };
     99
    100    /* Send client status */
    101    pthread_mutex_lock(&(rdp_client->message_lock));
    102    status = rail->ClientInformation(rail, &client_status);
    103    pthread_mutex_unlock(&(rdp_client->message_lock));
    104
    105    if (status != CHANNEL_RC_OK)
    106        return status;
    107
    108    RAIL_SYSPARAM_ORDER sysparam = {
    109
    110        .dragFullWindows = FALSE,
    111
    112        .highContrast = {
    113            .flags =
    114                  HCF_AVAILABLE
    115                | HCF_CONFIRMHOTKEY
    116                | HCF_HOTKEYACTIVE
    117                | HCF_HOTKEYAVAILABLE
    118                | HCF_HOTKEYSOUND
    119                | HCF_INDICATOR,
    120            .colorScheme = {
    121                .string = NULL,
    122                .length = 0
    123            }
    124        },
    125
    126        .keyboardCues = FALSE,
    127        .keyboardPref = FALSE,
    128        .mouseButtonSwap = FALSE,
    129
    130        .workArea = {
    131            .left   = 0,
    132            .top    = 0,
    133            .right  = rdp_client->settings->width,
    134            .bottom = rdp_client->settings->height
    135        },
    136
    137        .params =
    138              SPI_MASK_SET_DRAG_FULL_WINDOWS
    139            | SPI_MASK_SET_HIGH_CONTRAST
    140            | SPI_MASK_SET_KEYBOARD_CUES
    141            | SPI_MASK_SET_KEYBOARD_PREF
    142            | SPI_MASK_SET_MOUSE_BUTTON_SWAP
    143            | SPI_MASK_SET_WORK_AREA
    144
    145    };
    146
    147    /* Send client system parameters */
    148    pthread_mutex_lock(&(rdp_client->message_lock));
    149    status = rail->ClientSystemParam(rail, &sysparam);
    150    pthread_mutex_unlock(&(rdp_client->message_lock));
    151
    152    if (status != CHANNEL_RC_OK)
    153        return status;
    154
    155    RAIL_EXEC_ORDER exec = {
    156        .flags = RAIL_EXEC_FLAG_EXPAND_ARGUMENTS,
    157        .RemoteApplicationProgram = rdp_client->settings->remote_app,
    158        .RemoteApplicationWorkingDir = rdp_client->settings->remote_app_dir,
    159        .RemoteApplicationArguments = rdp_client->settings->remote_app_args,
    160    };
    161
    162    /* Execute desired RemoteApp command */
    163    pthread_mutex_lock(&(rdp_client->message_lock));
    164    status = rail->ClientExecute(rail, &exec);
    165    pthread_mutex_unlock(&(rdp_client->message_lock));
    166
    167    return status;
    168
    169}
    170
    171/**
    172 * Callback which is invoked when a Handshake PDU is received from the RDP
    173 * server. No communication for RemoteApp may occur until the Handshake PDU
    174 * (or, alternatively, the HandshakeEx PDU) is received. See:
    175 *
    176 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/cec4eb83-b304-43c9-8378-b5b8f5e7082a
    177 *
    178 * @param rail
    179 *     The RailClientContext structure used by FreeRDP to handle the RAIL
    180 *     channel for the current RDP session.
    181 *
    182 * @param handshake
    183 *     The RAIL_HANDSHAKE_ORDER structure representing the Handshake PDU that
    184 *     was received.
    185 *
    186 * @return
    187 *     CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
    188 *     (non-zero) otherwise.
    189 */
    190static UINT guac_rdp_rail_handshake(RailClientContext* rail,
    191        RAIL_CONST RAIL_HANDSHAKE_ORDER* handshake) {
    192    return guac_rdp_rail_complete_handshake(rail);
    193}
    194
    195/**
    196 * Callback which is invoked when a HandshakeEx PDU is received from the RDP
    197 * server. No communication for RemoteApp may occur until the HandshakeEx PDU
    198 * (or, alternatively, the Handshake PDU) is received. See:
    199 *
    200 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/5cec5414-27de-442e-8d4a-c8f8b41f3899
    201 *
    202 * @param rail
    203 *     The RailClientContext structure used by FreeRDP to handle the RAIL
    204 *     channel for the current RDP session.
    205 *
    206 * @param handshake_ex
    207 *     The RAIL_HANDSHAKE_EX_ORDER structure representing the HandshakeEx PDU
    208 *     that was received.
    209 *
    210 * @return
    211 *     CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code
    212 *     (non-zero) otherwise.
    213 */
    214static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail,
    215        RAIL_CONST RAIL_HANDSHAKE_EX_ORDER* handshake_ex) {
    216    return guac_rdp_rail_complete_handshake(rail);
    217}
    218
    219/**
    220 * Callback which associates handlers specific to Guacamole with the
    221 * RailClientContext instance allocated by FreeRDP to deal with received
    222 * RAIL (RemoteApp) messages.
    223 *
    224 * This function is called whenever a channel connects via the PubSub event
    225 * system within FreeRDP, but only has any effect if the connected channel is
    226 * the RAIL channel. This specific callback is registered with the PubSub
    227 * system of the relevant rdpContext when guac_rdp_rail_load_plugin() is
    228 * called.
    229 *
    230 * @param context
    231 *     The rdpContext associated with the active RDP session.
    232 *
    233 * @param e
    234 *     Event-specific arguments, mainly the name of the channel, and a
    235 *     reference to the associated plugin loaded for that channel by FreeRDP.
    236 */
    237static void guac_rdp_rail_channel_connected(rdpContext* context,
    238        ChannelConnectedEventArgs* e) {
    239
    240    guac_client* client = ((rdp_freerdp_context*) context)->client;
    241
    242    /* Ignore connection event if it's not for the RAIL channel */
    243    if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) != 0)
    244        return;
    245
    246    /* The structure pointed to by pInterface is guaranteed to be a
    247     * RailClientContext if the channel is RAIL */
    248    RailClientContext* rail = (RailClientContext*) e->pInterface;
    249
    250    /* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from
    251     * within any RAIL-specific callbacks */
    252    rail->custom = client;
    253    rail->ServerHandshake = guac_rdp_rail_handshake;
    254    rail->ServerHandshakeEx = guac_rdp_rail_handshake_ex;
    255
    256    guac_client_log(client, GUAC_LOG_DEBUG, "RAIL (RemoteApp) channel "
    257            "connected.");
    258
    259}
    260
    261void guac_rdp_rail_load_plugin(rdpContext* context) {
    262
    263    guac_client* client = ((rdp_freerdp_context*) context)->client;
    264
    265    /* Attempt to load FreeRDP support for the RAIL channel */
    266    if (guac_freerdp_channels_load_plugin(context, "rail", context->settings)) {
    267        guac_client_log(client, GUAC_LOG_WARNING,
    268                "Support for the RAIL channel (RemoteApp) could not be "
    269                "loaded. This support normally takes the form of a plugin "
    270                "which is built into FreeRDP. Lacking this support, "
    271                "RemoteApp will not work.");
    272        return;
    273    }
    274
    275    /* Complete RDP side of initialization when channel is connected */
    276    PubSub_SubscribeChannelConnected(context->pubSub,
    277            (pChannelConnectedEventHandler) guac_rdp_rail_channel_connected);
    278
    279    guac_client_log(client, GUAC_LOG_DEBUG, "Support for RAIL (RemoteApp) "
    280            "registered. Awaiting channel connection.");
    281
    282}
    283