cscg24-guacamole

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

upload.c (8436B)


      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 "fs.h"
     21#include "rdp.h"
     22#include "upload.h"
     23
     24#include <guacamole/client.h>
     25#include <guacamole/mem.h>
     26#include <guacamole/object.h>
     27#include <guacamole/protocol.h>
     28#include <guacamole/socket.h>
     29#include <guacamole/stream.h>
     30#include <guacamole/user.h>
     31#include <winpr/nt.h>
     32
     33#include <stdlib.h>
     34
     35/**
     36 * Writes the given filename to the given upload path, sanitizing the filename
     37 * and translating the filename to the root directory.
     38 *
     39 * @param filename
     40 *     The filename to sanitize and move to the root directory.
     41 *
     42 * @param path
     43 *     A pointer to a buffer which should receive the sanitized path. The
     44 *     buffer must have at least GUAC_RDP_FS_MAX_PATH bytes available.
     45 */
     46static void __generate_upload_path(const char* filename, char* path) {
     47
     48    int i;
     49
     50    /* Add initial backslash */
     51    *(path++) = '\\';
     52
     53    for (i=1; i<GUAC_RDP_FS_MAX_PATH; i++) {
     54
     55        /* Get current, stop at end */
     56        char c = *(filename++);
     57        if (c == '\0')
     58            break;
     59
     60        /* Replace special characters with underscores */
     61        if (c == '/' || c == '\\')
     62            c = '_';
     63
     64        *(path++) = c;
     65
     66    }
     67
     68    /* Terminate path */
     69    *path = '\0';
     70
     71}
     72
     73int guac_rdp_upload_file_handler(guac_user* user, guac_stream* stream,
     74        char* mimetype, char* filename) {
     75
     76    guac_client* client = user->client;
     77    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
     78
     79    int file_id;
     80    char file_path[GUAC_RDP_FS_MAX_PATH];
     81
     82    /* Get filesystem, return error if no filesystem */
     83    guac_rdp_fs* fs = rdp_client->filesystem;
     84    if (fs == NULL) {
     85        guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
     86                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
     87        guac_socket_flush(user->socket);
     88        return 0;
     89    }
     90
     91    /* Ignore upload if uploads have been disabled */
     92    if (fs->disable_upload) {
     93        guac_client_log(client, GUAC_LOG_WARNING, "A upload attempt has "
     94                "been blocked due to uploads being disabled, however it "
     95                "should have been blocked at a higher level. This is likely "
     96                "a bug.");
     97        guac_protocol_send_ack(user->socket, stream, "FAIL (UPLOAD DISABLED)",
     98                GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
     99        guac_socket_flush(user->socket);
    100        return 0;
    101    }
    102
    103    /* Translate name */
    104    __generate_upload_path(filename, file_path);
    105
    106    /* Open file */
    107    file_id = guac_rdp_fs_open(fs, file_path, GENERIC_WRITE, 0,
    108            FILE_OVERWRITE_IF, 0);
    109    if (file_id < 0) {
    110        guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
    111                GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
    112        guac_socket_flush(user->socket);
    113        return 0;
    114    }
    115
    116    /* Init upload status */
    117    guac_rdp_upload_status* upload_status = guac_mem_alloc(sizeof(guac_rdp_upload_status));
    118    upload_status->offset = 0;
    119    upload_status->file_id = file_id;
    120    stream->data = upload_status;
    121    stream->blob_handler = guac_rdp_upload_blob_handler;
    122    stream->end_handler = guac_rdp_upload_end_handler;
    123
    124    guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
    125            GUAC_PROTOCOL_STATUS_SUCCESS);
    126    guac_socket_flush(user->socket);
    127    return 0;
    128
    129}
    130
    131int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
    132        void* data, int length) {
    133
    134    int bytes_written;
    135    guac_rdp_upload_status* upload_status = (guac_rdp_upload_status*) stream->data;
    136
    137    /* Get filesystem, return error if no filesystem */
    138    guac_client* client = user->client;
    139    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    140    guac_rdp_fs* fs = rdp_client->filesystem;
    141    if (fs == NULL) {
    142        guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
    143                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
    144        guac_socket_flush(user->socket);
    145        return 0;
    146    }
    147
    148    /* Write entire block */
    149    while (length > 0) {
    150
    151        /* Attempt write */
    152        bytes_written = guac_rdp_fs_write(fs, upload_status->file_id,
    153                upload_status->offset, data, length);
    154
    155        /* On error, abort */
    156        if (bytes_written < 0) {
    157            guac_protocol_send_ack(user->socket, stream,
    158                    "FAIL (BAD WRITE)",
    159                    GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
    160            guac_socket_flush(user->socket);
    161            return 0;
    162        }
    163
    164        /* Update counters */
    165        upload_status->offset += bytes_written;
    166        data += bytes_written;
    167        length -= bytes_written;
    168
    169    }
    170
    171    guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
    172            GUAC_PROTOCOL_STATUS_SUCCESS);
    173    guac_socket_flush(user->socket);
    174    return 0;
    175
    176}
    177
    178int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
    179
    180    guac_client* client = user->client;
    181    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    182    guac_rdp_upload_status* upload_status = (guac_rdp_upload_status*) stream->data;
    183
    184    /* Get filesystem, return error if no filesystem */
    185    guac_rdp_fs* fs = rdp_client->filesystem;
    186    if (fs == NULL) {
    187        guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
    188                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
    189        guac_socket_flush(user->socket);
    190        return 0;
    191    }
    192
    193    /* Close file */
    194    guac_rdp_fs_close(fs, upload_status->file_id);
    195
    196    /* Acknowledge stream end */
    197    guac_protocol_send_ack(user->socket, stream, "OK (STREAM END)",
    198            GUAC_PROTOCOL_STATUS_SUCCESS);
    199    guac_socket_flush(user->socket);
    200
    201    guac_mem_free(upload_status);
    202    return 0;
    203
    204}
    205
    206int guac_rdp_upload_put_handler(guac_user* user, guac_object* object,
    207        guac_stream* stream, char* mimetype, char* name) {
    208
    209    guac_client* client = user->client;
    210    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    211
    212    /* Get filesystem, return error if no filesystem */
    213    guac_rdp_fs* fs = rdp_client->filesystem;
    214    if (fs == NULL) {
    215        guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
    216                GUAC_PROTOCOL_STATUS_SERVER_ERROR);
    217        guac_socket_flush(user->socket);
    218        return 0;
    219    }
    220
    221    /* Ignore upload if uploads have been disabled */
    222    if (fs->disable_upload) {
    223        guac_client_log(client, GUAC_LOG_WARNING, "A upload attempt has "
    224                "been blocked due to uploads being disabled, however it "
    225                "should have been blocked at a higher level. This is likely "
    226                "a bug.");
    227        guac_protocol_send_ack(user->socket, stream, "FAIL (UPLOAD DISABLED)",
    228                GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
    229        guac_socket_flush(user->socket);
    230        return 0;
    231    }
    232
    233    /* Open file */
    234    int file_id = guac_rdp_fs_open(fs, name, GENERIC_WRITE, 0,
    235            FILE_OVERWRITE_IF, 0);
    236
    237    /* Abort on failure */
    238    if (file_id < 0) {
    239        guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
    240                GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
    241        guac_socket_flush(user->socket);
    242        return 0;
    243    }
    244
    245    /* Init upload stream data */
    246    guac_rdp_upload_status* upload_status = guac_mem_alloc(sizeof(guac_rdp_upload_status));
    247    upload_status->offset = 0;
    248    upload_status->file_id = file_id;
    249
    250    /* Allocate stream, init for file upload */
    251    stream->data = upload_status;
    252    stream->blob_handler = guac_rdp_upload_blob_handler;
    253    stream->end_handler = guac_rdp_upload_end_handler;
    254
    255    /* Acknowledge stream creation */
    256    guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
    257            GUAC_PROTOCOL_STATUS_SUCCESS);
    258    guac_socket_flush(user->socket);
    259    return 0;
    260}
    261