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