1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/socket.h> 32 #include <sys/types.h> 33 #include <sys/sysctl.h> 34 #include <sys/stat.h> 35 36 #include <netinet/in.h> 37 #include <arpa/tftp.h> 38 39 #include <ctype.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <syslog.h> 44 45 #include "tftp-utils.h" 46 #include "tftp-io.h" 47 #include "tftp-options.h" 48 49 /* 50 * Option handlers 51 */ 52 53 struct options options[] = { 54 { "tsize", NULL, NULL, NULL /* option_tsize */, 1 }, 55 { "timeout", NULL, NULL, option_timeout, 1 }, 56 { "blksize", NULL, NULL, option_blksize, 1 }, 57 { "blksize2", NULL, NULL, option_blksize2, 0 }, 58 { "rollover", NULL, NULL, option_rollover, 0 }, 59 { NULL, NULL, NULL, NULL, 0 } 60 }; 61 62 /* By default allow them */ 63 int options_rfc_enabled = 1; 64 int options_extra_enabled = 1; 65 66 /* 67 * Rules for the option handlers: 68 * - If there is no o_request, there will be no processing. 69 * 70 * For servers 71 * - Logging is done as warnings. 72 * - The handler exit()s if there is a serious problem with the 73 * values submitted in the option. 74 * 75 * For clients 76 * - Logging is done as errors. After all, the server shouldn't 77 * return rubbish. 78 * - The handler returns if there is a serious problem with the 79 * values submitted in the option. 80 * - Sending the EBADOP packets is done by the handler. 81 */ 82 83 int 84 option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode, 85 struct stat *stbuf) 86 { 87 88 if (options[OPT_TSIZE].o_request == NULL) 89 return (0); 90 91 if (mode == RRQ) 92 asprintf(&options[OPT_TSIZE].o_reply, 93 "%ju", stbuf->st_size); 94 else 95 /* XXX Allows writes of all sizes. */ 96 options[OPT_TSIZE].o_reply = 97 strdup(options[OPT_TSIZE].o_request); 98 return (0); 99 } 100 101 int 102 option_timeout(int peer) 103 { 104 int to; 105 106 if (options[OPT_TIMEOUT].o_request == NULL) 107 return (0); 108 109 to = atoi(options[OPT_TIMEOUT].o_request); 110 if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) { 111 tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, 112 "Received bad value for timeout. " 113 "Should be between %d and %d, received %d", 114 TIMEOUT_MIN, TIMEOUT_MAX, to); 115 send_error(peer, EBADOP); 116 if (acting_as_client) 117 return (1); 118 exit(1); 119 } else { 120 timeoutpacket = to; 121 options[OPT_TIMEOUT].o_reply = 122 strdup(options[OPT_TIMEOUT].o_request); 123 } 124 settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts); 125 126 if (debug&DEBUG_OPTIONS) 127 tftp_log(LOG_DEBUG, "Setting timeout to '%s'", 128 options[OPT_TIMEOUT].o_reply); 129 130 return (0); 131 } 132 133 int 134 option_rollover(int peer) 135 { 136 137 if (options[OPT_ROLLOVER].o_request == NULL) 138 return (0); 139 140 if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0 141 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) { 142 tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, 143 "Bad value for rollover, " 144 "should be either 0 or 1, received '%s', " 145 "ignoring request", 146 options[OPT_ROLLOVER].o_request); 147 if (acting_as_client) { 148 send_error(peer, EBADOP); 149 return (1); 150 } 151 return (0); 152 } 153 options[OPT_ROLLOVER].o_reply = 154 strdup(options[OPT_ROLLOVER].o_request); 155 156 if (debug&DEBUG_OPTIONS) 157 tftp_log(LOG_DEBUG, "Setting rollover to '%s'", 158 options[OPT_ROLLOVER].o_reply); 159 160 return (0); 161 } 162 163 int 164 option_blksize(int peer) 165 { 166 u_long maxdgram; 167 size_t len; 168 169 if (options[OPT_BLKSIZE].o_request == NULL) 170 return (0); 171 172 /* maximum size of an UDP packet according to the system */ 173 len = sizeof(maxdgram); 174 if (sysctlbyname("net.inet.udp.maxdgram", 175 &maxdgram, &len, NULL, 0) < 0) { 176 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); 177 return (acting_as_client ? 1 : 0); 178 } 179 180 int size = atoi(options[OPT_BLKSIZE].o_request); 181 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { 182 if (acting_as_client) { 183 tftp_log(LOG_ERR, 184 "Invalid blocksize (%d bytes), aborting", 185 size); 186 send_error(peer, EBADOP); 187 return (1); 188 } else { 189 tftp_log(LOG_WARNING, 190 "Invalid blocksize (%d bytes), ignoring request", 191 size); 192 return (0); 193 } 194 } 195 196 if (size > (int)maxdgram) { 197 if (acting_as_client) { 198 tftp_log(LOG_ERR, 199 "Invalid blocksize (%d bytes), " 200 "net.inet.udp.maxdgram sysctl limits it to " 201 "%ld bytes.\n", size, maxdgram); 202 send_error(peer, EBADOP); 203 return (1); 204 } else { 205 tftp_log(LOG_WARNING, 206 "Invalid blocksize (%d bytes), " 207 "net.inet.udp.maxdgram sysctl limits it to " 208 "%ld bytes.\n", size, maxdgram); 209 size = maxdgram; 210 /* No reason to return */ 211 } 212 } 213 214 asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size); 215 segsize = size; 216 pktsize = size + 4; 217 if (debug&DEBUG_OPTIONS) 218 tftp_log(LOG_DEBUG, "Setting blksize to '%s'", 219 options[OPT_BLKSIZE].o_reply); 220 221 return (0); 222 } 223 224 int 225 option_blksize2(int peer __unused) 226 { 227 u_long maxdgram; 228 int size, i; 229 size_t len; 230 231 int sizes[] = { 232 8, 16, 32, 64, 128, 256, 512, 1024, 233 2048, 4096, 8192, 16384, 32768, 0 234 }; 235 236 if (options[OPT_BLKSIZE2].o_request == NULL) 237 return (0); 238 239 /* maximum size of an UDP packet according to the system */ 240 len = sizeof(maxdgram); 241 if (sysctlbyname("net.inet.udp.maxdgram", 242 &maxdgram, &len, NULL, 0) < 0) { 243 tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); 244 return (acting_as_client ? 1 : 0); 245 } 246 247 size = atoi(options[OPT_BLKSIZE2].o_request); 248 for (i = 0; sizes[i] != 0; i++) { 249 if (size == sizes[i]) break; 250 } 251 if (sizes[i] == 0) { 252 tftp_log(LOG_INFO, 253 "Invalid blocksize2 (%d bytes), ignoring request", size); 254 return (acting_as_client ? 1 : 0); 255 } 256 257 if (size > (int)maxdgram) { 258 for (i = 0; sizes[i+1] != 0; i++) { 259 if ((int)maxdgram < sizes[i+1]) break; 260 } 261 tftp_log(LOG_INFO, 262 "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram " 263 "sysctl limits it to %ld bytes.\n", size, maxdgram); 264 size = sizes[i]; 265 /* No need to return */ 266 } 267 268 asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size); 269 segsize = size; 270 pktsize = size + 4; 271 if (debug&DEBUG_OPTIONS) 272 tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'", 273 options[OPT_BLKSIZE2].o_reply); 274 275 return (0); 276 } 277 278 /* 279 * Append the available options to the header 280 */ 281 uint16_t 282 make_options(int peer __unused, char *buffer, uint16_t size) { 283 int i; 284 char *value; 285 const char *option; 286 uint16_t length; 287 uint16_t returnsize = 0; 288 289 if (!options_rfc_enabled) return (0); 290 291 for (i = 0; options[i].o_type != NULL; i++) { 292 if (options[i].rfc == 0 && !options_extra_enabled) 293 continue; 294 295 option = options[i].o_type; 296 if (acting_as_client) 297 value = options[i].o_request; 298 else 299 value = options[i].o_reply; 300 if (value == NULL) 301 continue; 302 303 length = strlen(value) + strlen(option) + 2; 304 if (size <= length) { 305 tftp_log(LOG_ERR, 306 "Running out of option space for " 307 "option '%s' with value '%s': " 308 "needed %d bytes, got %d bytes", 309 option, value, size, length); 310 continue; 311 } 312 313 sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000'); 314 size -= length; 315 buffer += length; 316 returnsize += length; 317 } 318 319 return (returnsize); 320 } 321 322 /* 323 * Parse the received options in the header 324 */ 325 int 326 parse_options(int peer, char *buffer, uint16_t size) 327 { 328 int i, options_failed; 329 char *c, *cp, *option, *value; 330 331 if (!options_rfc_enabled) return (0); 332 333 /* Parse the options */ 334 cp = buffer; 335 options_failed = 0; 336 while (size > 0) { 337 option = cp; 338 i = get_field(peer, cp, size); 339 cp += i; 340 341 value = cp; 342 i = get_field(peer, cp, size); 343 cp += i; 344 345 /* We are at the end */ 346 if (*option == '\0') break; 347 348 if (debug&DEBUG_OPTIONS) 349 tftp_log(LOG_DEBUG, 350 "option: '%s' value: '%s'", option, value); 351 352 for (c = option; *c; c++) 353 if (isupper(*c)) 354 *c = tolower(*c); 355 for (i = 0; options[i].o_type != NULL; i++) { 356 if (strcmp(option, options[i].o_type) == 0) { 357 if (!acting_as_client) 358 options[i].o_request = value; 359 if (!options_extra_enabled && !options[i].rfc) { 360 tftp_log(LOG_INFO, 361 "Option '%s' with value '%s' found " 362 "but it is not an RFC option", 363 option, value); 364 continue; 365 } 366 if (options[i].o_handler) 367 options_failed += 368 (options[i].o_handler)(peer); 369 break; 370 } 371 } 372 if (options[i].o_type == NULL) 373 tftp_log(LOG_WARNING, 374 "Unknown option: '%s'", option); 375 376 size -= strlen(option) + strlen(value) + 2; 377 } 378 379 return (options_failed); 380 } 381 382 /* 383 * Set some default values in the options 384 */ 385 void 386 init_options(void) 387 { 388 389 options[OPT_ROLLOVER].o_request = strdup("0"); 390 } 391