1 /* 2 * Copyright (c) 1988, 1992 The University of Utah and the Center 3 * for Software Science (CSS). 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * the Center for Software Science of the University of Utah Computer 9 * Science Department. CSS requests users of this software to return 10 * to css-dist@cs.utah.edu any improvements that they make and grant 11 * CSS redistribution rights. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 38 * 39 * From: Utah Hdr: rmpproto.c 3.1 92/07/06 40 * Author: Jeff Forys, University of Utah CSS 41 */ 42 43 #ifndef lint 44 #if 0 45 static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; 46 #endif 47 static const char rcsid[] = 48 "$FreeBSD$"; 49 #endif /* not lint */ 50 51 #include <sys/param.h> 52 #include <sys/time.h> 53 #include <netinet/in.h> 54 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <stdio.h> 58 #include <string.h> 59 #include <syslog.h> 60 #include <unistd.h> 61 #include "defs.h" 62 63 /* 64 ** ProcessPacket -- determine packet type and do what's required. 65 ** 66 ** An RMP BOOT packet has been received. Look at the type field 67 ** and process Boot Requests, Read Requests, and Boot Complete 68 ** packets. Any other type will be dropped with a warning msg. 69 ** 70 ** Parameters: 71 ** rconn - the new connection 72 ** client - list of files available to this host 73 ** 74 ** Returns: 75 ** Nothing. 76 ** 77 ** Side Effects: 78 ** - If this is a valid boot request, it will be added to 79 ** the linked list of outstanding requests (RmpConns). 80 ** - If this is a valid boot complete, its associated 81 ** entry in RmpConns will be deleted. 82 ** - Also, unless we run out of memory, a reply will be 83 ** sent to the host that sent the packet. 84 */ 85 void 86 ProcessPacket(RMPCONN *rconn, CLIENT *client) 87 { 88 struct rmp_packet *rmp; 89 RMPCONN *rconnout; 90 91 rmp = &rconn->rmp; /* cache pointer to RMP packet */ 92 93 switch(rmp->r_type) { /* do what we came here to do */ 94 case RMP_BOOT_REQ: /* boot request */ 95 if ((rconnout = NewConn(rconn)) == NULL) 96 return; 97 98 /* 99 * If the Session ID is 0xffff, this is a "probe" 100 * packet and we do not want to add the connection 101 * to the linked list of active connections. There 102 * are two types of probe packets, if the Sequence 103 * Number is 0 they want to know our host name, o/w 104 * they want the name of the file associated with 105 * the number spec'd by the Sequence Number. 106 * 107 * If this is an actual boot request, open the file 108 * and send a reply. If SendBootRepl() does not 109 * return 0, add the connection to the linked list 110 * of active connections, otherwise delete it since 111 * an error was encountered. 112 */ 113 if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { 114 if (WORDZE(rmp->r_brq.rmp_seqno)) 115 (void) SendServerID(rconnout); 116 else 117 (void) SendFileNo(rmp, rconnout, 118 client? client->files: 119 BootFiles); 120 FreeConn(rconnout); 121 } else { 122 if (SendBootRepl(rmp, rconnout, 123 client? client->files: BootFiles)) 124 AddConn(rconnout); 125 else 126 FreeConn(rconnout); 127 } 128 break; 129 130 case RMP_BOOT_REPL: /* boot reply (not valid) */ 131 syslog(LOG_WARNING, "%s: sent a boot reply", 132 EnetStr(rconn)); 133 break; 134 135 case RMP_READ_REQ: /* read request */ 136 /* 137 * Send a portion of the boot file. 138 */ 139 (void) SendReadRepl(rconn); 140 break; 141 142 case RMP_READ_REPL: /* read reply (not valid) */ 143 syslog(LOG_WARNING, "%s: sent a read reply", 144 EnetStr(rconn)); 145 break; 146 147 case RMP_BOOT_DONE: /* boot complete */ 148 /* 149 * Remove the entry from the linked list of active 150 * connections. 151 */ 152 (void) BootDone(rconn); 153 break; 154 155 default: /* unknown RMP packet type */ 156 syslog(LOG_WARNING, "%s: unknown packet type (%u)", 157 EnetStr(rconn), rmp->r_type); 158 } 159 } 160 161 /* 162 ** SendServerID -- send our host name to who ever requested it. 163 ** 164 ** Parameters: 165 ** rconn - the reply packet to be formatted. 166 ** 167 ** Returns: 168 ** 1 on success, 0 on failure. 169 ** 170 ** Side Effects: 171 ** none. 172 */ 173 int 174 SendServerID(RMPCONN *rconn) 175 { 176 struct rmp_packet *rpl; 177 char *src, *dst; 178 u_int8_t *size; 179 180 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 181 182 /* 183 * Set up assorted fields in reply packet. 184 */ 185 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 186 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 187 ZEROWORD(rpl->r_brpl.rmp_seqno); 188 rpl->r_brpl.rmp_session = 0; 189 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 190 191 size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ 192 193 /* 194 * Copy our host name into the reply packet incrementing the 195 * length as we go. Stop at RMP_HOSTLEN or the first dot. 196 */ 197 src = MyHost; 198 dst = (char *) &rpl->r_brpl.rmp_flnm; 199 for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { 200 if (*src == '.' || *src == '\0') 201 break; 202 *dst++ = *src++; 203 } 204 205 rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ 206 207 return(SendPacket(rconn)); /* send packet */ 208 } 209 210 /* 211 ** SendFileNo -- send the name of a bootable file to the requester. 212 ** 213 ** Parameters: 214 ** req - RMP BOOT packet containing the request. 215 ** rconn - the reply packet to be formatted. 216 ** filelist - list of files available to the requester. 217 ** 218 ** Returns: 219 ** 1 on success, 0 on failure. 220 ** 221 ** Side Effects: 222 ** none. 223 */ 224 int 225 SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) 226 { 227 struct rmp_packet *rpl; 228 char *src, *dst; 229 u_int8_t *size; 230 int i; 231 232 GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ 233 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 234 235 /* 236 * Set up assorted fields in reply packet. 237 */ 238 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 239 PUTWORD(i, rpl->r_brpl.rmp_seqno); 240 i--; 241 rpl->r_brpl.rmp_session = 0; 242 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 243 244 size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ 245 *size = 0; /* init length to zero */ 246 247 /* 248 * Copy the file name into the reply packet incrementing the 249 * length as we go. Stop at end of string or when RMPBOOTDATA 250 * characters have been copied. Also, set return code to 251 * indicate success or "no more files". 252 */ 253 if (i < C_MAXFILE && filelist[i] != NULL) { 254 src = filelist[i]; 255 dst = (char *)&rpl->r_brpl.rmp_flnm; 256 for (; *src && *size < RMPBOOTDATA; (*size)++) { 257 if (*src == '\0') 258 break; 259 *dst++ = *src++; 260 } 261 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 262 } else 263 rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; 264 265 rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ 266 267 return(SendPacket(rconn)); /* send packet */ 268 } 269 270 /* 271 ** SendBootRepl -- open boot file and respond to boot request. 272 ** 273 ** Parameters: 274 ** req - RMP BOOT packet containing the request. 275 ** rconn - the reply packet to be formatted. 276 ** filelist - list of files available to the requester. 277 ** 278 ** Returns: 279 ** 1 on success, 0 on failure. 280 ** 281 ** Side Effects: 282 ** none. 283 */ 284 int 285 SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) 286 { 287 int retval; 288 char *filename, filepath[RMPBOOTDATA+1]; 289 RMPCONN *oldconn; 290 struct rmp_packet *rpl; 291 char *src, *dst1, *dst2; 292 u_int8_t i; 293 294 /* 295 * If another connection already exists, delete it since we 296 * are obviously starting again. 297 */ 298 if ((oldconn = FindConn(rconn)) != NULL) { 299 syslog(LOG_WARNING, "%s: dropping existing connection", 300 EnetStr(oldconn)); 301 RemoveConn(oldconn); 302 } 303 304 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 305 306 /* 307 * Set up assorted fields in reply packet. 308 */ 309 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 310 COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); 311 rpl->r_brpl.rmp_session = htons(GenSessID()); 312 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 313 rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; 314 315 /* 316 * Copy file name to `filepath' string, and into reply packet. 317 */ 318 src = &req->r_brq.rmp_flnm; 319 dst1 = filepath; 320 dst2 = &rpl->r_brpl.rmp_flnm; 321 for (i = 0; i < req->r_brq.rmp_flnmsize; i++) 322 *dst1++ = *dst2++ = *src++; 323 *dst1 = '\0'; 324 325 /* 326 * If we are booting HP-UX machines, their secondary loader will 327 * ask for files like "/hp-ux". As a security measure, we do not 328 * allow boot files to lay outside the boot directory (unless they 329 * are purposely link'd out. So, make `filename' become the path- 330 * stripped file name and spoof the client into thinking that it 331 * really got what it wanted. 332 */ 333 filename = (filename = strrchr(filepath,'/'))? ++filename: filepath; 334 335 /* 336 * Check that this is a valid boot file name. 337 */ 338 for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) 339 if (STREQN(filename, filelist[i])) 340 goto match; 341 342 /* 343 * Invalid boot file name, set error and send reply packet. 344 */ 345 rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; 346 retval = 0; 347 goto sendpkt; 348 349 match: 350 /* 351 * This is a valid boot file. Open the file and save the file 352 * descriptor associated with this connection and set success 353 * indication. If the file couldnt be opened, set error: 354 * "no such file or dir" - RMP_E_NOFILE 355 * "file table overflow" - RMP_E_BUSY 356 * "too many open files" - RMP_E_BUSY 357 * anything else - RMP_E_OPENFILE 358 */ 359 if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { 360 rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: 361 (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: 362 RMP_E_OPENFILE; 363 retval = 0; 364 } else { 365 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 366 retval = 1; 367 } 368 369 sendpkt: 370 syslog(LOG_INFO, "%s: request to boot %s (%s)", 371 EnetStr(rconn), filename, retval? "granted": "denied"); 372 373 rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); 374 375 return (retval & SendPacket(rconn)); 376 } 377 378 /* 379 ** SendReadRepl -- send a portion of the boot file to the requester. 380 ** 381 ** Parameters: 382 ** rconn - the reply packet to be formatted. 383 ** 384 ** Returns: 385 ** 1 on success, 0 on failure. 386 ** 387 ** Side Effects: 388 ** none. 389 */ 390 int 391 SendReadRepl(RMPCONN *rconn) 392 { 393 int retval = 0; 394 RMPCONN *oldconn; 395 struct rmp_packet *rpl, *req; 396 int size = 0; 397 int madeconn = 0; 398 399 /* 400 * Find the old connection. If one doesn't exist, create one only 401 * to return the error code. 402 */ 403 if ((oldconn = FindConn(rconn)) == NULL) { 404 if ((oldconn = NewConn(rconn)) == NULL) 405 return(0); 406 syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", 407 EnetStr(rconn)); 408 madeconn++; 409 } 410 411 req = &rconn->rmp; /* cache ptr to request packet */ 412 rpl = &oldconn->rmp; /* cache ptr to reply packet */ 413 414 if (madeconn) { /* no active connection above; abort */ 415 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 416 retval = 1; 417 goto sendpkt; 418 } 419 420 /* 421 * Make sure Session ID's match. 422 */ 423 if (ntohs(req->r_rrq.rmp_session) != 424 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 425 ntohs(rpl->r_rrpl.rmp_session))) { 426 syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", 427 EnetStr(rconn)); 428 rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; 429 retval = 1; 430 goto sendpkt; 431 } 432 433 /* 434 * If the requester asks for more data than we can fit, 435 * silently clamp the request size down to RMPREADDATA. 436 * 437 * N.B. I do not know if this is "legal", however it seems 438 * to work. This is necessary for bpfwrite() on machines 439 * with MCLBYTES less than 1514. 440 */ 441 if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) 442 req->r_rrq.rmp_size = htons(RMPREADDATA); 443 444 /* 445 * Position read head on file according to info in request packet. 446 */ 447 GETWORD(req->r_rrq.rmp_offset, size); 448 if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) { 449 syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", 450 EnetStr(rconn)); 451 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 452 retval = 1; 453 goto sendpkt; 454 } 455 456 /* 457 * Read data directly into reply packet. 458 */ 459 if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, 460 (int) ntohs(req->r_rrq.rmp_size))) <= 0) { 461 if (size < 0) { 462 syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", 463 EnetStr(rconn)); 464 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 465 } else { 466 rpl->r_rrpl.rmp_retcode = RMP_E_EOF; 467 } 468 retval = 1; 469 goto sendpkt; 470 } 471 472 /* 473 * Set success indication. 474 */ 475 rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; 476 477 sendpkt: 478 /* 479 * Set up assorted fields in reply packet. 480 */ 481 rpl->r_rrpl.rmp_type = RMP_READ_REPL; 482 COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); 483 rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; 484 485 oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ 486 487 retval &= SendPacket(oldconn); /* send packet */ 488 489 if (madeconn) /* clean up after ourself */ 490 FreeConn(oldconn); 491 492 return (retval); 493 } 494 495 /* 496 ** BootDone -- free up memory allocated for a connection. 497 ** 498 ** Parameters: 499 ** rconn - incoming boot complete packet. 500 ** 501 ** Returns: 502 ** 1 on success, 0 on failure. 503 ** 504 ** Side Effects: 505 ** none. 506 */ 507 int 508 BootDone(RMPCONN *rconn) 509 { 510 RMPCONN *oldconn; 511 struct rmp_packet *rpl; 512 513 /* 514 * If we can't find the connection, ignore the request. 515 */ 516 if ((oldconn = FindConn(rconn)) == NULL) { 517 syslog(LOG_ERR, "BootDone: no existing connection (%s)", 518 EnetStr(rconn)); 519 return(0); 520 } 521 522 rpl = &oldconn->rmp; /* cache ptr to RMP packet */ 523 524 /* 525 * Make sure Session ID's match. 526 */ 527 if (ntohs(rconn->rmp.r_rrq.rmp_session) != 528 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 529 ntohs(rpl->r_rrpl.rmp_session))) { 530 syslog(LOG_ERR, "BootDone: bad session id (%s)", 531 EnetStr(rconn)); 532 return(0); 533 } 534 535 RemoveConn(oldconn); /* remove connection */ 536 537 syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); 538 539 return(1); 540 } 541 542 /* 543 ** SendPacket -- send an RMP packet to a remote host. 544 ** 545 ** Parameters: 546 ** rconn - packet to be sent. 547 ** 548 ** Returns: 549 ** 1 on success, 0 on failure. 550 ** 551 ** Side Effects: 552 ** none. 553 */ 554 int 555 SendPacket(RMPCONN *rconn) 556 { 557 /* 558 * Set Ethernet Destination address to Source (BPF and the enet 559 * driver will take care of getting our source address set). 560 */ 561 memmove((char *)&rconn->rmp.hp_hdr.daddr[0], 562 (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN); 563 rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); 564 565 /* 566 * Reverse 802.2/HP Extended Source & Destination Access Pts. 567 */ 568 rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); 569 rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); 570 571 /* 572 * Last time this connection was active. 573 */ 574 (void)gettimeofday(&rconn->tstamp, NULL); 575 576 if (DbgFp != NULL) /* display packet */ 577 DispPkt(rconn,DIR_SENT); 578 579 /* 580 * Send RMP packet to remote host. 581 */ 582 return(BpfWrite(rconn)); 583 } 584