tftpd.c revision 1.3
1/* 2 * Copyright (c) 1983 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35char copyright[] = 36"@(#) Copyright (c) 1983 Regents of the University of California.\n\ 37 All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/ 42static char rcsid[] = "$Id: tftpd.c,v 1.3 1993/08/01 18:28:53 mycroft Exp $"; 43#endif /* not lint */ 44 45/* 46 * Trivial file transfer protocol server. 47 * 48 * This version includes many modifications by Jim Guyton <guyton@rand-unix> 49 */ 50 51#include <sys/types.h> 52#include <sys/ioctl.h> 53#include <sys/stat.h> 54#include <signal.h> 55#include <fcntl.h> 56 57#include <sys/socket.h> 58#include <netinet/in.h> 59#include <arpa/tftp.h> 60#include <netdb.h> 61 62#include <setjmp.h> 63#include <syslog.h> 64#include <stdio.h> 65#include <errno.h> 66#include <ctype.h> 67#include <string.h> 68#include <stdlib.h> 69 70#define TIMEOUT 5 71 72extern int errno; 73struct sockaddr_in s_in = { AF_INET }; 74int peer; 75int rexmtval = TIMEOUT; 76int maxtimeout = 5*TIMEOUT; 77 78#define PKTSIZE SEGSIZE+4 79char buf[PKTSIZE]; 80char ackbuf[PKTSIZE]; 81struct sockaddr_in from; 82int fromlen; 83 84#define MAXARG 4 85char *dirs[MAXARG+1]; 86 87main(ac, av) 88 char **av; 89{ 90 register struct tftphdr *tp; 91 register int n = 0; 92 int on = 1; 93 94 ac--; av++; 95 while (ac-- > 0 && n < MAXARG) 96 dirs[n++] = *av++; 97 openlog("tftpd", LOG_PID, LOG_DAEMON); 98 if (ioctl(0, FIONBIO, &on) < 0) { 99 syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); 100 exit(1); 101 } 102 fromlen = sizeof (from); 103 n = recvfrom(0, buf, sizeof (buf), 0, 104 (struct sockaddr *)&from, &fromlen); 105 if (n < 0) { 106 syslog(LOG_ERR, "recvfrom: %m\n"); 107 exit(1); 108 } 109 /* 110 * Now that we have read the message out of the UDP 111 * socket, we fork and exit. Thus, inetd will go back 112 * to listening to the tftp port, and the next request 113 * to come in will start up a new instance of tftpd. 114 * 115 * We do this so that inetd can run tftpd in "wait" mode. 116 * The problem with tftpd running in "nowait" mode is that 117 * inetd may get one or more successful "selects" on the 118 * tftp port before we do our receive, so more than one 119 * instance of tftpd may be started up. Worse, if tftpd 120 * break before doing the above "recvfrom", inetd would 121 * spawn endless instances, clogging the system. 122 */ 123 { 124 int pid; 125 int i, j; 126 127 for (i = 1; i < 20; i++) { 128 pid = fork(); 129 if (pid < 0) { 130 sleep(i); 131 /* 132 * flush out to most recently sent request. 133 * 134 * This may drop some request, but those 135 * will be resent by the clients when 136 * they timeout. The positive effect of 137 * this flush is to (try to) prevent more 138 * than one tftpd being started up to service 139 * a single request from a single client. 140 */ 141 j = sizeof from; 142 i = recvfrom(0, buf, sizeof (buf), 0, 143 (struct sockaddr *)&from, &j); 144 if (i > 0) { 145 n = i; 146 fromlen = j; 147 } 148 } else { 149 break; 150 } 151 } 152 if (pid < 0) { 153 syslog(LOG_ERR, "fork: %m\n"); 154 exit(1); 155 } else if (pid != 0) { 156 exit(0); 157 } 158 } 159 from.sin_family = AF_INET; 160 alarm(0); 161 close(0); 162 close(1); 163 peer = socket(AF_INET, SOCK_DGRAM, 0); 164 if (peer < 0) { 165 syslog(LOG_ERR, "socket: %m\n"); 166 exit(1); 167 } 168 if (bind(peer, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { 169 syslog(LOG_ERR, "bind: %m\n"); 170 exit(1); 171 } 172 if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { 173 syslog(LOG_ERR, "connect: %m\n"); 174 exit(1); 175 } 176 tp = (struct tftphdr *)buf; 177 tp->th_opcode = ntohs(tp->th_opcode); 178 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 179 tftp(tp, n); 180 exit(1); 181} 182 183int validate_access(); 184int sendfile(), recvfile(); 185 186struct formats { 187 char *f_mode; 188 int (*f_validate)(); 189 int (*f_send)(); 190 int (*f_recv)(); 191 int f_convert; 192} formats[] = { 193 { "netascii", validate_access, sendfile, recvfile, 1 }, 194 { "octet", validate_access, sendfile, recvfile, 0 }, 195#ifdef notdef 196 { "mail", validate_user, sendmail, recvmail, 1 }, 197#endif 198 { 0 } 199}; 200 201/* 202 * Handle initial connection protocol. 203 */ 204tftp(tp, size) 205 struct tftphdr *tp; 206 int size; 207{ 208 register char *cp; 209 int first = 1, ecode; 210 register struct formats *pf; 211 char *filename, *mode; 212 213 filename = cp = tp->th_stuff; 214again: 215 while (cp < buf + size) { 216 if (*cp == '\0') 217 break; 218 cp++; 219 } 220 if (*cp != '\0') { 221 nak(EBADOP); 222 exit(1); 223 } 224 if (first) { 225 mode = ++cp; 226 first = 0; 227 goto again; 228 } 229 for (cp = mode; *cp; cp++) 230 if (isupper(*cp)) 231 *cp = tolower(*cp); 232 for (pf = formats; pf->f_mode; pf++) 233 if (strcmp(pf->f_mode, mode) == 0) 234 break; 235 if (pf->f_mode == 0) { 236 nak(EBADOP); 237 exit(1); 238 } 239 ecode = (*pf->f_validate)(filename, tp->th_opcode); 240 if (ecode) { 241 nak(ecode); 242 exit(1); 243 } 244 if (tp->th_opcode == WRQ) 245 (*pf->f_recv)(pf); 246 else 247 (*pf->f_send)(pf); 248 exit(0); 249} 250 251 252FILE *file; 253 254/* 255 * Validate file access. Since we 256 * have no uid or gid, for now require 257 * file to exist and be publicly 258 * readable/writable. 259 * If we were invoked with arguments 260 * from inetd then the file must also be 261 * in one of the given directory prefixes. 262 * Note also, full path name must be 263 * given as we have no login directory. 264 */ 265validate_access(filename, mode) 266 char *filename; 267 int mode; 268{ 269 struct stat stbuf; 270 int fd; 271 char *cp, **dirp; 272 273 if (*filename != '/') 274 return (EACCESS); 275 /* 276 * prevent tricksters from getting around the directory restrictions 277 */ 278 for (cp = filename + 1; *cp; cp++) 279 if(*cp == '.' && strncmp(cp-1, "/../", 4) == 0) 280 return(EACCESS); 281 for (dirp = dirs; *dirp; dirp++) 282 if (strncmp(filename, *dirp, strlen(*dirp)) == 0) 283 break; 284 if (*dirp==0 && dirp!=dirs) 285 return (EACCESS); 286 if (stat(filename, &stbuf) < 0) 287 return (errno == ENOENT ? ENOTFOUND : EACCESS); 288 if (mode == RRQ) { 289 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) 290 return (EACCESS); 291 } else { 292 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) 293 return (EACCESS); 294 } 295 fd = open(filename, mode == RRQ ? 0 : 1); 296 if (fd < 0) 297 return (errno + 100); 298 file = fdopen(fd, (mode == RRQ)? "r":"w"); 299 if (file == NULL) { 300 return errno+100; 301 } 302 return (0); 303} 304 305int timeout; 306jmp_buf timeoutbuf; 307 308void 309timer() 310{ 311 312 timeout += rexmtval; 313 if (timeout >= maxtimeout) 314 exit(1); 315 longjmp(timeoutbuf, 1); 316} 317 318/* 319 * Send the requested file. 320 */ 321sendfile(pf) 322 struct formats *pf; 323{ 324 struct tftphdr *dp, *r_init(); 325 register struct tftphdr *ap; /* ack packet */ 326 register int block = 1, size, n; 327 328 signal(SIGALRM, timer); 329 dp = r_init(); 330 ap = (struct tftphdr *)ackbuf; 331 do { 332 size = readit(file, &dp, pf->f_convert); 333 if (size < 0) { 334 nak(errno + 100); 335 goto abort; 336 } 337 dp->th_opcode = htons((u_short)DATA); 338 dp->th_block = htons((u_short)block); 339 timeout = 0; 340 (void) setjmp(timeoutbuf); 341 342send_data: 343 if (send(peer, dp, size + 4, 0) != size + 4) { 344 syslog(LOG_ERR, "tftpd: write: %m\n"); 345 goto abort; 346 } 347 read_ahead(file, pf->f_convert); 348 for ( ; ; ) { 349 alarm(rexmtval); /* read the ack */ 350 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 351 alarm(0); 352 if (n < 0) { 353 syslog(LOG_ERR, "tftpd: read: %m\n"); 354 goto abort; 355 } 356 ap->th_opcode = ntohs((u_short)ap->th_opcode); 357 ap->th_block = ntohs((u_short)ap->th_block); 358 359 if (ap->th_opcode == ERROR) 360 goto abort; 361 362 if (ap->th_opcode == ACK) { 363 if (ap->th_block == block) { 364 break; 365 } 366 /* Re-synchronize with the other side */ 367 (void) synchnet(peer); 368 if (ap->th_block == (block -1)) { 369 goto send_data; 370 } 371 } 372 373 } 374 block++; 375 } while (size == SEGSIZE); 376abort: 377 (void) fclose(file); 378} 379 380void 381justquit() 382{ 383 exit(0); 384} 385 386 387/* 388 * Receive a file. 389 */ 390recvfile(pf) 391 struct formats *pf; 392{ 393 struct tftphdr *dp, *w_init(); 394 register struct tftphdr *ap; /* ack buffer */ 395 register int block = 0, n, size; 396 397 signal(SIGALRM, timer); 398 dp = w_init(); 399 ap = (struct tftphdr *)ackbuf; 400 do { 401 timeout = 0; 402 ap->th_opcode = htons((u_short)ACK); 403 ap->th_block = htons((u_short)block); 404 block++; 405 (void) setjmp(timeoutbuf); 406send_ack: 407 if (send(peer, ackbuf, 4, 0) != 4) { 408 syslog(LOG_ERR, "tftpd: write: %m\n"); 409 goto abort; 410 } 411 write_behind(file, pf->f_convert); 412 for ( ; ; ) { 413 alarm(rexmtval); 414 n = recv(peer, dp, PKTSIZE, 0); 415 alarm(0); 416 if (n < 0) { /* really? */ 417 syslog(LOG_ERR, "tftpd: read: %m\n"); 418 goto abort; 419 } 420 dp->th_opcode = ntohs((u_short)dp->th_opcode); 421 dp->th_block = ntohs((u_short)dp->th_block); 422 if (dp->th_opcode == ERROR) 423 goto abort; 424 if (dp->th_opcode == DATA) { 425 if (dp->th_block == block) { 426 break; /* normal */ 427 } 428 /* Re-synchronize with the other side */ 429 (void) synchnet(peer); 430 if (dp->th_block == (block-1)) 431 goto send_ack; /* rexmit */ 432 } 433 } 434 /* size = write(file, dp->th_data, n - 4); */ 435 size = writeit(file, &dp, n - 4, pf->f_convert); 436 if (size != (n-4)) { /* ahem */ 437 if (size < 0) nak(errno + 100); 438 else nak(ENOSPACE); 439 goto abort; 440 } 441 } while (size == SEGSIZE); 442 write_behind(file, pf->f_convert); 443 (void) fclose(file); /* close data file */ 444 445 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 446 ap->th_block = htons((u_short)(block)); 447 (void) send(peer, ackbuf, 4, 0); 448 449 signal(SIGALRM, justquit); /* just quit on timeout */ 450 alarm(rexmtval); 451 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 452 alarm(0); 453 if (n >= 4 && /* if read some data */ 454 dp->th_opcode == DATA && /* and got a data block */ 455 block == dp->th_block) { /* then my last ack was lost */ 456 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 457 } 458abort: 459 return; 460} 461 462struct errmsg { 463 int e_code; 464 char *e_msg; 465} errmsgs[] = { 466 { EUNDEF, "Undefined error code" }, 467 { ENOTFOUND, "File not found" }, 468 { EACCESS, "Access violation" }, 469 { ENOSPACE, "Disk full or allocation exceeded" }, 470 { EBADOP, "Illegal TFTP operation" }, 471 { EBADID, "Unknown transfer ID" }, 472 { EEXISTS, "File already exists" }, 473 { ENOUSER, "No such user" }, 474 { -1, 0 } 475}; 476 477/* 478 * Send a nak packet (error message). 479 * Error code passed in is one of the 480 * standard TFTP codes, or a UNIX errno 481 * offset by 100. 482 */ 483nak(error) 484 int error; 485{ 486 register struct tftphdr *tp; 487 int length; 488 register struct errmsg *pe; 489 490 tp = (struct tftphdr *)buf; 491 tp->th_opcode = htons((u_short)ERROR); 492 tp->th_code = htons((u_short)error); 493 for (pe = errmsgs; pe->e_code >= 0; pe++) 494 if (pe->e_code == error) 495 break; 496 if (pe->e_code < 0) { 497 pe->e_msg = strerror(error - 100); 498 tp->th_code = EUNDEF; /* set 'undef' errorcode */ 499 } 500 strcpy(tp->th_msg, pe->e_msg); 501 length = strlen(pe->e_msg); 502 tp->th_msg[length] = '\0'; 503 length += 5; 504 if (send(peer, buf, length, 0) != length) 505 syslog(LOG_ERR, "nak: %m\n"); 506} 507