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