1 /* 2 * tftpd - tftp service, see /lib/rfc/rfc783 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <auth.h> 7 #include <bio.h> 8 #include <ip.h> 9 #include <ndb.h> 10 11 enum 12 { 13 Maxpath= 128, 14 Maxerr= 256, 15 }; 16 17 int dbg; 18 int restricted; 19 void sendfile(int, char*, char*); 20 void recvfile(int, char*, char*); 21 void nak(int, int, char*); 22 void ack(int, ushort); 23 void clrcon(void); 24 void setuser(void); 25 char* sunkernel(char*); 26 void remoteaddr(char*, char*, int); 27 void doserve(int); 28 29 char bigbuf[32768]; 30 char raddr[64]; 31 32 char *dir = "/lib/tftpd"; 33 char *dirsl; 34 int dirsllen; 35 char flog[] = "ipboot"; 36 char net[Maxpath]; 37 38 enum 39 { 40 Tftp_READ = 1, 41 Tftp_WRITE = 2, 42 Tftp_DATA = 3, 43 Tftp_ACK = 4, 44 Tftp_ERROR = 5, 45 Segsize = 512, 46 }; 47 48 void 49 usage(void) 50 { 51 fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n", 52 argv0); 53 exits("usage"); 54 } 55 56 void 57 main(int argc, char **argv) 58 { 59 char buf[64]; 60 char adir[64], ldir[64]; 61 int cfd, lcfd, dfd; 62 char *svc = "69"; 63 64 setnetmtpt(net, sizeof net, nil); 65 ARGBEGIN{ 66 case 'd': 67 dbg++; 68 break; 69 case 'h': 70 dir = EARGF(usage()); 71 break; 72 case 'r': 73 restricted = 1; 74 break; 75 case 's': 76 svc = EARGF(usage()); 77 break; 78 case 'x': 79 setnetmtpt(net, sizeof net, EARGF(usage())); 80 break; 81 default: 82 usage(); 83 }ARGEND 84 85 snprint(buf, sizeof buf, "%s/", dir); 86 dirsl = strdup(buf); 87 dirsllen = strlen(dirsl); 88 89 fmtinstall('E', eipfmt); 90 fmtinstall('I', eipfmt); 91 92 if(chdir(dir) < 0) 93 sysfatal("can't get to directory %s: %r", dir); 94 95 if(!dbg) 96 switch(rfork(RFNOTEG|RFPROC|RFFDG)) { 97 case -1: 98 sysfatal("fork: %r"); 99 case 0: 100 break; 101 default: 102 exits(0); 103 } 104 105 snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc); 106 cfd = announce(buf, adir); 107 if (cfd < 0) 108 sysfatal("announcing on %s: %r", buf); 109 syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir); 110 setuser(); 111 for(;;) { 112 lcfd = listen(adir, ldir); 113 if(lcfd < 0) 114 sysfatal("listening on %s: %r", adir); 115 116 switch(fork()) { 117 case -1: 118 sysfatal("fork: %r"); 119 case 0: 120 dfd = accept(lcfd, ldir); 121 if(dfd < 0) 122 exits(0); 123 remoteaddr(ldir, raddr, sizeof(raddr)); 124 syslog(0, flog, "tftp connection from %s dir %s", 125 raddr, ldir); 126 doserve(dfd); 127 exits("done"); 128 break; 129 default: 130 close(lcfd); 131 continue; 132 } 133 } 134 } 135 136 void 137 doserve(int fd) 138 { 139 int dlen; 140 char *mode, *p; 141 short op; 142 143 dlen = read(fd, bigbuf, sizeof(bigbuf)); 144 if(dlen < 0) 145 sysfatal("listen read: %r"); 146 147 op = (bigbuf[0]<<8) | bigbuf[1]; 148 dlen -= 2; 149 mode = bigbuf+2; 150 while(*mode != '\0' && dlen--) 151 mode++; 152 mode++; 153 p = mode; 154 while(*p && dlen--) 155 p++; 156 if(dlen == 0) { 157 nak(fd, 0, "bad tftpmode"); 158 close(fd); 159 syslog(dbg, flog, "bad mode from %s", raddr); 160 return; 161 } 162 163 if(op != Tftp_READ && op != Tftp_WRITE) { 164 nak(fd, 4, "Illegal TFTP operation"); 165 close(fd); 166 syslog(dbg, flog, "bad request %d %s", op, raddr); 167 return; 168 } 169 170 if(restricted){ 171 if(bigbuf[2] == '#' || 172 strncmp(bigbuf+2, "../", 3)==0 || strstr(bigbuf+2, "/../") || 173 (bigbuf[2] == '/' && strncmp(bigbuf+2, dirsl, dirsllen)!=0)){ 174 nak(fd, 4, "Permission denied"); 175 close(fd); 176 syslog(dbg, flog, "bad request %d from %s file %s", op, raddr, bigbuf+2); 177 return; 178 } 179 } 180 181 if(op == Tftp_READ) 182 sendfile(fd, bigbuf+2, mode); 183 else 184 recvfile(fd, bigbuf+2, mode); 185 } 186 187 void 188 catcher(void *junk, char *msg) 189 { 190 USED(junk); 191 192 if(strncmp(msg, "exit", 4) == 0) 193 noted(NDFLT); 194 noted(NCONT); 195 } 196 197 void 198 sendfile(int fd, char *name, char *mode) 199 { 200 int file; 201 uchar buf[Segsize+4]; 202 uchar ack[1024]; 203 char errbuf[Maxerr]; 204 int ackblock, block, ret; 205 int rexmit, n, al, txtry, rxl; 206 short op; 207 208 syslog(dbg, flog, "send file '%s' %s to %s", name, mode, raddr); 209 name = sunkernel(name); 210 if(name == 0){ 211 nak(fd, 0, "not in our database"); 212 return; 213 } 214 215 notify(catcher); 216 217 file = open(name, OREAD); 218 if(file < 0) { 219 errstr(errbuf, sizeof errbuf); 220 nak(fd, 0, errbuf); 221 return; 222 } 223 block = 0; 224 rexmit = 0; 225 n = 0; 226 for(txtry = 0; txtry < 5;) { 227 if(rexmit == 0) { 228 block++; 229 buf[0] = 0; 230 buf[1] = Tftp_DATA; 231 buf[2] = block>>8; 232 buf[3] = block; 233 n = read(file, buf+4, Segsize); 234 if(n < 0) { 235 errstr(errbuf, sizeof errbuf); 236 nak(fd, 0, errbuf); 237 return; 238 } 239 txtry = 0; 240 } 241 else { 242 syslog(dbg, flog, "rexmit %d %s:%d to %s", 243 4+n, name, block, raddr); 244 txtry++; 245 } 246 247 ret = write(fd, buf, 4+n); 248 if(ret < 0) 249 sysfatal("tftpd: network write error: %r"); 250 251 for(rxl = 0; rxl < 10; rxl++) { 252 rexmit = 0; 253 alarm(500); 254 al = read(fd, ack, sizeof(ack)); 255 alarm(0); 256 if(al < 0) { 257 rexmit = 1; 258 break; 259 } 260 op = ack[0]<<8|ack[1]; 261 if(op == Tftp_ERROR) 262 goto error; 263 ackblock = ack[2]<<8|ack[3]; 264 if(ackblock == block) 265 break; 266 if(ackblock == 0xffff) { 267 rexmit = 1; 268 break; 269 } 270 } 271 if(ret != Segsize+4 && rexmit == 0) 272 break; 273 } 274 error: 275 close(fd); 276 close(file); 277 } 278 279 enum { Hdrsize = 2 * sizeof(short), }; /* op, block */ 280 281 void 282 recvfile(int fd, char *name, char *mode) 283 { 284 ushort op, block, inblock; 285 uchar buf[Segsize+8]; 286 char errbuf[Maxerr]; 287 int n, ret, file; 288 289 syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr); 290 291 file = create(name, OWRITE, 0666); 292 if(file < 0) { 293 errstr(errbuf, sizeof errbuf); 294 nak(fd, 0, errbuf); 295 syslog(dbg, flog, "can't create %s: %r", name); 296 return; 297 } 298 299 block = 0; 300 ack(fd, block); 301 block++; 302 303 for (;;) { 304 alarm(15000); 305 n = read(fd, buf, sizeof(buf)); 306 alarm(0); 307 if(n < 0) { 308 syslog(dbg, flog, "tftpd: network error reading %s: %r", 309 name); 310 goto error; 311 } 312 if(n <= Hdrsize) { 313 syslog(dbg, flog, 314 "tftpd: short read from network, reading %s", 315 name); 316 goto error; 317 } 318 op = buf[0]<<8|buf[1]; 319 if(op == Tftp_ERROR) { 320 syslog(dbg, flog, "tftpd: tftp error reading %s", name); 321 goto error; 322 } 323 324 n -= Hdrsize; 325 inblock = buf[2]<<8|buf[3]; 326 if(op == Tftp_DATA) { 327 if(inblock == block) { 328 ret = write(file, buf+Hdrsize, n); 329 if(ret != n) { 330 errstr(errbuf, sizeof errbuf); 331 nak(fd, 0, errbuf); 332 syslog(dbg, flog, 333 "tftpd: error writing %s: %s", 334 name, errbuf); 335 goto error; 336 } 337 ack(fd, block); 338 block++; 339 } else 340 ack(fd, 0xffff); /* tell him to resend */ 341 } 342 } 343 error: 344 close(file); 345 } 346 347 void 348 ack(int fd, ushort block) 349 { 350 uchar ack[4]; 351 int n; 352 353 ack[0] = 0; 354 ack[1] = Tftp_ACK; 355 ack[2] = block>>8; 356 ack[3] = block; 357 358 n = write(fd, ack, 4); 359 if(n < 4) 360 sysfatal("network write: %r"); 361 } 362 363 void 364 nak(int fd, int code, char *msg) 365 { 366 char buf[128]; 367 int n; 368 369 buf[0] = 0; 370 buf[1] = Tftp_ERROR; 371 buf[2] = 0; 372 buf[3] = code; 373 strcpy(buf+4, msg); 374 n = strlen(msg) + 4 + 1; 375 n = write(fd, buf, n); 376 if(n < 0) 377 sysfatal("write nak: %r"); 378 } 379 380 void 381 setuser(void) 382 { 383 int fd; 384 385 fd = open("#c/user", OWRITE); 386 if(fd < 0 || write(fd, "none", strlen("none")) < 0) 387 sysfatal("can't become none: %r"); 388 close(fd); 389 if(newns("none", nil) < 0) 390 sysfatal("can't build namespace: %r"); 391 } 392 393 char* 394 lookup(char *sattr, char *sval, char *tattr, char *tval, int len) 395 { 396 static Ndb *db; 397 char *attrs[1]; 398 Ndbtuple *t; 399 400 if(db == nil) 401 db = ndbopen(0); 402 if(db == nil) 403 return nil; 404 405 if(sattr == nil) 406 sattr = ipattr(sval); 407 408 attrs[0] = tattr; 409 t = ndbipinfo(db, sattr, sval, attrs, 1); 410 if(t == nil) 411 return nil; 412 strncpy(tval, t->val, len); 413 tval[len-1] = 0; 414 ndbfree(t); 415 return tval; 416 } 417 418 /* 419 * for sun kernel boots, replace the requested file name with 420 * a one from our database. If the database doesn't specify a file, 421 * don't answer. 422 */ 423 char* 424 sunkernel(char *name) 425 { 426 ulong addr; 427 uchar v4[IPv4addrlen]; 428 uchar v6[IPaddrlen]; 429 char buf[256]; 430 char ipbuf[128]; 431 char *suffix; 432 433 addr = strtoul(name, &suffix, 16); 434 if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0)) 435 return name; 436 437 v4[0] = addr>>24; 438 v4[1] = addr>>16; 439 v4[2] = addr>>8; 440 v4[3] = addr; 441 v4tov6(v6, v4); 442 sprint(ipbuf, "%I", v6); 443 return lookup("ip", ipbuf, "bootf", buf, sizeof buf); 444 } 445 446 void 447 remoteaddr(char *dir, char *raddr, int len) 448 { 449 char buf[64]; 450 int fd, n; 451 452 snprint(buf, sizeof(buf), "%s/remote", dir); 453 fd = open(buf, OREAD); 454 if(fd < 0){ 455 snprint(raddr, sizeof(raddr), "unknown"); 456 return; 457 } 458 n = read(fd, raddr, len-1); 459 close(fd); 460 if(n <= 0){ 461 snprint(raddr, sizeof(raddr), "unknown"); 462 return; 463 } 464 if(n > 0) 465 n--; 466 raddr[n] = 0; 467 } 468