cscg24-guacamole

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

rdpdr-messages.c (14925B)


      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/rdpdr/rdpdr-messages.h"
     21#include "channels/rdpdr/rdpdr.h"
     22#include "rdp.h"
     23#include "settings.h"
     24
     25#include <freerdp/channels/rdpdr.h>
     26#include <guacamole/client.h>
     27#include <winpr/stream.h>
     28
     29#include <stdlib.h>
     30#include <string.h>
     31
     32/**
     33 * Sends a Client Announce Reply message. The Client Announce Reply message is
     34 * required to be sent in response to the Server Announce Request message. See:
     35 *
     36 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/d6fe6d1b-c145-4a6f-99aa-4fe3cdcea398
     37 *
     38 * @param svc
     39 *     The guac_rdp_common_svc representing the static virtual channel being
     40 *     used for RDPDR.
     41 *
     42 * @param major
     43 *     The major version of the RDPDR protocol in use. This value must always
     44 *     be 1.
     45 *
     46 * @param minor
     47 *     The minor version of the RDPDR protocol in use. This value must be
     48 *     either 2, 5, 10, 12, or 13.
     49 *
     50 * @param client_id
     51 *     The client ID received in the Server Announce Request, or a randomly
     52 *     generated ID.
     53 */
     54static void guac_rdpdr_send_client_announce_reply(guac_rdp_common_svc* svc,
     55        unsigned int major, unsigned int minor, unsigned int client_id) {
     56
     57    wStream* output_stream = Stream_New(NULL, 12);
     58
     59    /* Write header */
     60    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
     61    Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENTID_CONFIRM);
     62
     63    /* Write content */
     64    Stream_Write_UINT16(output_stream, major);
     65    Stream_Write_UINT16(output_stream, minor);
     66    Stream_Write_UINT32(output_stream, client_id);
     67
     68    guac_rdp_common_svc_write(svc, output_stream);
     69
     70}
     71
     72/**
     73 * Sends a Client Name Request message. The Client Name Request message is used
     74 * by the client to announce its own name. See:
     75 *
     76 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/902497f1-3b1c-4aee-95f8-1668f9b7b7d2
     77 *
     78 * @param svc
     79 *     The guac_rdp_common_svc representing the static virtual channel being
     80 *     used for RDPDR.
     81 *
     82 * @param name
     83 *     The name that should be used for the client.
     84 */
     85static void guac_rdpdr_send_client_name_request(guac_rdp_common_svc* svc,
     86        const char* name) {
     87
     88    int name_bytes = strlen(name) + 1;
     89    wStream* output_stream = Stream_New(NULL, 16 + name_bytes);
     90
     91    /* Write header */
     92    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
     93    Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_NAME);
     94
     95    /* Write content */
     96    Stream_Write_UINT32(output_stream, 0); /* ASCII */
     97    Stream_Write_UINT32(output_stream, 0); /* 0 required by RDPDR spec */
     98    Stream_Write_UINT32(output_stream, name_bytes);
     99    Stream_Write(output_stream, name, name_bytes);
    100
    101    guac_rdp_common_svc_write(svc, output_stream);
    102
    103}
    104
    105/**
    106 * Sends a Client Core Capability Response message. The Client Core Capability
    107 * Response message is used to announce the client's capabilities, in response
    108 * to receiving the server's capabilities via a Server Core Capability Request.
    109 * See:
    110 *
    111 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/f513bf87-cca0-488a-ac5c-18cf18f4a7e1
    112 *
    113 * @param svc
    114 *     The guac_rdp_common_svc representing the static virtual channel being
    115 *     used for RDPDR.
    116 */
    117static void guac_rdpdr_send_client_capability(guac_rdp_common_svc* svc) {
    118
    119    wStream* output_stream = Stream_New(NULL, 256);
    120    guac_client_log(svc->client, GUAC_LOG_DEBUG, "Sending capabilities...");
    121
    122    /* Write header */
    123    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
    124    Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_CAPABILITY);
    125
    126    /* Capability count + padding */
    127    Stream_Write_UINT16(output_stream, 3);
    128    Stream_Write_UINT16(output_stream, 0); /* Padding */
    129
    130    /* General capability header */
    131    Stream_Write_UINT16(output_stream, CAP_GENERAL_TYPE);
    132    Stream_Write_UINT16(output_stream, 44);
    133    Stream_Write_UINT32(output_stream, GENERAL_CAPABILITY_VERSION_02);
    134
    135    /* General capability data */
    136    Stream_Write_UINT32(output_stream, GUAC_OS_TYPE);                /* osType - required to be ignored */
    137    Stream_Write_UINT32(output_stream, 0);                           /* osVersion */
    138    Stream_Write_UINT16(output_stream, 1);                           /* protocolMajor - must be set to 1 */
    139    Stream_Write_UINT16(output_stream, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinor */
    140    Stream_Write_UINT32(output_stream, 0xFFFF);                      /* ioCode1 */
    141    Stream_Write_UINT32(output_stream, 0);                           /* ioCode2 */
    142    Stream_Write_UINT32(output_stream,
    143                                      RDPDR_DEVICE_REMOVE_PDUS
    144                                    | RDPDR_CLIENT_DISPLAY_NAME_PDU
    145                                    | RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
    146    Stream_Write_UINT32(output_stream, 0);                      /* extraFlags1 */
    147    Stream_Write_UINT32(output_stream, 0);                      /* extraFlags2 */
    148    Stream_Write_UINT32(output_stream, 0);                      /* SpecialTypeDeviceCap */
    149
    150    /* Printer support header */
    151    Stream_Write_UINT16(output_stream, CAP_PRINTER_TYPE);
    152    Stream_Write_UINT16(output_stream, 8);
    153    Stream_Write_UINT32(output_stream, PRINT_CAPABILITY_VERSION_01);
    154
    155    /* Drive support header */
    156    Stream_Write_UINT16(output_stream, CAP_DRIVE_TYPE);
    157    Stream_Write_UINT16(output_stream, 8);
    158    Stream_Write_UINT32(output_stream, DRIVE_CAPABILITY_VERSION_02);
    159
    160    guac_rdp_common_svc_write(svc, output_stream);
    161    guac_client_log(svc->client, GUAC_LOG_DEBUG, "Capabilities sent.");
    162
    163}
    164
    165/**
    166 * Sends a Client Device List Announce Request message. The Client Device List
    167 * Announce Request message is used by the client to enumerate all devices
    168 * which should be made available within the RDP session via RDPDR. See:
    169 *
    170 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/10ef9ada-cba2-4384-ab60-7b6290ed4a9a
    171 *
    172 * @param svc
    173 *     The guac_rdp_common_svc representing the static virtual channel being
    174 *     used for RDPDR.
    175 */
    176static void guac_rdpdr_send_client_device_list_announce_request(guac_rdp_common_svc* svc) {
    177
    178    guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
    179
    180    /* Calculate number of bytes needed for the stream */
    181    int streamBytes = 16;
    182    for (int i=0; i < rdpdr->devices_registered; i++)
    183        streamBytes += rdpdr->devices[i].device_announce_len;
    184
    185    /* Allocate the stream */
    186    wStream* output_stream = Stream_New(NULL, streamBytes);
    187
    188    /* Write header */
    189    Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE);
    190    Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICELIST_ANNOUNCE);
    191
    192    /* Get the stream for each of the devices. */
    193    Stream_Write_UINT32(output_stream, rdpdr->devices_registered);
    194    for (int i=0; i<rdpdr->devices_registered; i++) {
    195        
    196        Stream_Write(output_stream,
    197                Stream_Buffer(rdpdr->devices[i].device_announce),
    198                rdpdr->devices[i].device_announce_len);
    199
    200        guac_client_log(svc->client, GUAC_LOG_DEBUG, "Registered device %i (%s)",
    201                rdpdr->devices[i].device_id, rdpdr->devices[i].device_name);
    202        
    203    }
    204
    205    guac_rdp_common_svc_write(svc, output_stream);
    206    guac_client_log(svc->client, GUAC_LOG_DEBUG, "All supported devices sent.");
    207
    208}
    209
    210void guac_rdpdr_process_server_announce(guac_rdp_common_svc* svc,
    211        wStream* input_stream) {
    212
    213    unsigned int major, minor, client_id;
    214
    215    /* Stream should contain at least 8 bytes (UINT16 + UINT16 + UINT32) */
    216    if (Stream_GetRemainingLength(input_stream) < 8) {
    217        guac_client_log(svc->client, GUAC_LOG_WARNING, "Server Announce "
    218                "Request PDU does not contain the expected number of bytes. "
    219                "Device redirection may not work as expected.");
    220        return;
    221    }
    222    
    223    Stream_Read_UINT16(input_stream, major);
    224    Stream_Read_UINT16(input_stream, minor);
    225    Stream_Read_UINT32(input_stream, client_id);
    226
    227    /* Must choose own client ID if minor not >= 12 */
    228    if (minor < 12)
    229        client_id = random() & 0xFFFF;
    230
    231    guac_client_log(svc->client, GUAC_LOG_INFO, "Connected to RDPDR %u.%u as client 0x%04x", major, minor, client_id);
    232
    233    /* Respond to announce */
    234    guac_rdpdr_send_client_announce_reply(svc, major, minor, client_id);
    235
    236    /* Name request */
    237    guac_rdpdr_send_client_name_request(svc, ((guac_rdp_client*) svc->client->data)->settings->client_name);
    238
    239}
    240
    241void guac_rdpdr_process_clientid_confirm(guac_rdp_common_svc* svc,
    242        wStream* input_stream) {
    243    guac_client_log(svc->client, GUAC_LOG_DEBUG, "Client ID confirmed");
    244}
    245
    246void guac_rdpdr_process_device_reply(guac_rdp_common_svc* svc,
    247        wStream* input_stream) {
    248
    249    guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
    250
    251    unsigned int device_id, ntstatus;
    252    int severity, c, n, facility, code;
    253
    254    /* Stream should contain at least 8 bytes (UINT32 + UINT32 ) */
    255    if (Stream_GetRemainingLength(input_stream) < 8) {
    256        guac_client_log(svc->client, GUAC_LOG_WARNING, "Server Device Announce"
    257                "Response PDU does not contain the expected number of bytes."
    258                "Device redirection may not work as expected.");
    259        return;
    260    }
    261    
    262    Stream_Read_UINT32(input_stream, device_id);
    263    Stream_Read_UINT32(input_stream, ntstatus);
    264
    265    severity = (ntstatus & 0xC0000000) >> 30;
    266    c        = (ntstatus & 0x20000000) >> 29;
    267    n        = (ntstatus & 0x10000000) >> 28;
    268    facility = (ntstatus & 0x0FFF0000) >> 16;
    269    code     =  ntstatus & 0x0000FFFF;
    270
    271    /* Log error / information */
    272    if (device_id < rdpdr->devices_registered) {
    273
    274        if (severity == 0x0)
    275            guac_client_log(svc->client, GUAC_LOG_DEBUG, "Device %i (%s) connected successfully",
    276                    device_id, rdpdr->devices[device_id].device_name);
    277
    278        else
    279            guac_client_log(svc->client, GUAC_LOG_ERROR, "Problem connecting device %i (%s): "
    280                    "severity=0x%x, c=0x%x, n=0x%x, facility=0x%x, code=0x%x",
    281                     device_id, rdpdr->devices[device_id].device_name,
    282                     severity,      c,      n,      facility,      code);
    283
    284    }
    285
    286    else
    287        guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown device ID: 0x%08x", device_id);
    288
    289}
    290
    291void guac_rdpdr_process_device_iorequest(guac_rdp_common_svc* svc,
    292        wStream* input_stream) {
    293
    294    guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
    295    guac_rdpdr_iorequest iorequest;
    296
    297    /* Check to make sure the Stream contains at least 20 bytes (5 x UINT32 ). */
    298    if (Stream_GetRemainingLength(input_stream) < 20) {
    299        guac_client_log(svc->client, GUAC_LOG_WARNING, "Device I/O Request PDU "
    300                "does not contain the expected number of bytes. Device "
    301                "redirection may not work as expected.");
    302        return;
    303    }
    304    
    305    /* Read header */
    306    Stream_Read_UINT32(input_stream, iorequest.device_id);
    307    Stream_Read_UINT32(input_stream, iorequest.file_id);
    308    Stream_Read_UINT32(input_stream, iorequest.completion_id);
    309    Stream_Read_UINT32(input_stream, iorequest.major_func);
    310    Stream_Read_UINT32(input_stream, iorequest.minor_func);
    311
    312    /* If printer, run printer handlers */
    313    if (iorequest.device_id >= 0 && iorequest.device_id < rdpdr->devices_registered) {
    314
    315        /* Call handler on device */
    316        guac_rdpdr_device* device = &(rdpdr->devices[iorequest.device_id]);
    317        device->iorequest_handler(svc, device, &iorequest, input_stream);
    318
    319    }
    320
    321    else
    322        guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown device ID: "
    323                "0x%08x", iorequest.device_id);
    324
    325}
    326
    327void guac_rdpdr_process_server_capability(guac_rdp_common_svc* svc,
    328        wStream* input_stream) {
    329
    330    int count;
    331    int i;
    332
    333    /* Check to make sure the Stream has at least 4 bytes (UINT16 + 2) */
    334    if (Stream_GetRemainingLength(input_stream) < 4) {
    335        guac_client_log(svc->client, GUAC_LOG_WARNING, "Server Core Capability "
    336                "Request PDU does not contain the expected number of bytes."
    337                "Device redirection may not work as expected.");
    338        return;
    339    }
    340    
    341    /* Read header */
    342    Stream_Read_UINT16(input_stream, count);
    343    Stream_Seek(input_stream, 2);
    344
    345    /* Parse capabilities */
    346    for (i=0; i<count; i++) {
    347
    348        int type;
    349        int length;
    350
    351        /* Make sure Stream has at least 4 bytes (UINT16 + UINT16) */
    352        if (Stream_GetRemainingLength(input_stream) < 4) {
    353            guac_client_log(svc->client, GUAC_LOG_WARNING, "Server Core "
    354                    "Capability Request PDU does not contain the expected "
    355                    "number of bytes. Device redirection may not work as "
    356                    "expected.");
    357            break;
    358        }
    359        
    360        Stream_Read_UINT16(input_stream, type);
    361        Stream_Read_UINT16(input_stream, length);
    362
    363        /* Make sure Stream has required length remaining for Seek below. */
    364        if (Stream_GetRemainingLength(input_stream) < (length - 4)) {
    365            guac_client_log(svc->client, GUAC_LOG_WARNING, "Server Core "
    366                    "Capability Request PDU does not contain the expected "
    367                    "number of bytes. Device redirection may not work as "
    368                    "expected.");
    369            break;
    370        }
    371        
    372        /* Ignore all for now */
    373        guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring server capability set type=0x%04x, length=%i", type, length);
    374        Stream_Seek(input_stream, length - 4);
    375
    376    }
    377
    378    /* Send own capabilities */
    379    guac_rdpdr_send_client_capability(svc);
    380
    381}
    382
    383void guac_rdpdr_process_user_loggedon(guac_rdp_common_svc* svc,
    384        wStream* input_stream) {
    385
    386    guac_client_log(svc->client, GUAC_LOG_INFO, "RDPDR user logged on");
    387    guac_rdpdr_send_client_device_list_announce_request(svc);
    388
    389}
    390
    391void guac_rdpdr_process_prn_cache_data(guac_rdp_common_svc* svc,
    392        wStream* input_stream) {
    393    guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring printer cached configuration data");
    394}
    395
    396void guac_rdpdr_process_prn_using_xps(guac_rdp_common_svc* svc,
    397        wStream* input_stream) {
    398    guac_client_log(svc->client, GUAC_LOG_WARNING, "Printer unexpectedly switched to XPS mode");
    399}