1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1988, 1992 The University of Utah and the Center 5 * for Software Science (CSS). 6 * Copyright (c) 1992, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * the Center for Software Science of the University of Utah Computer 11 * Science Department. CSS requests users of this software to return 12 * to css-dist@cs.utah.edu any improvements that they make and grant 13 * CSS redistribution rights. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 3. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 40 * 41 * From: Utah Hdr: rmpproto.c 3.1 92/07/06 42 * Author: Jeff Forys, University of Utah CSS 43 */ 44 45 #ifndef lint 46 #if 0 47 static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; 48 #endif 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 = strrchr(filepath,'/'); 334 filename = filename? filename + 1: filepath; 335 336 /* 337 * Check that this is a valid boot file name. 338 */ 339 for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) 340 if (STREQN(filename, filelist[i])) 341 goto match; 342 343 /* 344 * Invalid boot file name, set error and send reply packet. 345 */ 346 rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; 347 retval = 0; 348 goto sendpkt; 349 350 match: 351 /* 352 * This is a valid boot file. Open the file and save the file 353 * descriptor associated with this connection and set success 354 * indication. If the file couldnt be opened, set error: 355 * "no such file or dir" - RMP_E_NOFILE 356 * "file table overflow" - RMP_E_BUSY 357 * "too many open files" - RMP_E_BUSY 358 * anything else - RMP_E_OPENFILE 359 */ 360 if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { 361 rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: 362 (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: 363 RMP_E_OPENFILE; 364 retval = 0; 365 } else { 366 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 367 retval = 1; 368 } 369 370 sendpkt: 371 syslog(LOG_INFO, "%s: request to boot %s (%s)", 372 EnetStr(rconn), filename, retval? "granted": "denied"); 373 374 rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); 375 376 return (retval & SendPacket(rconn)); 377 } 378 379 /* 380 ** SendReadRepl -- send a portion of the boot file to the requester. 381 ** 382 ** Parameters: 383 ** rconn - the reply packet to be formatted. 384 ** 385 ** Returns: 386 ** 1 on success, 0 on failure. 387 ** 388 ** Side Effects: 389 ** none. 390 */ 391 int 392 SendReadRepl(RMPCONN *rconn) 393 { 394 int retval = 0; 395 RMPCONN *oldconn; 396 struct rmp_packet *rpl, *req; 397 int size = 0; 398 int madeconn = 0; 399 400 /* 401 * Find the old connection. If one doesn't exist, create one only 402 * to return the error code. 403 */ 404 if ((oldconn = FindConn(rconn)) == NULL) { 405 if ((oldconn = NewConn(rconn)) == NULL) 406 return(0); 407 syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", 408 EnetStr(rconn)); 409 madeconn++; 410 } 411 412 req = &rconn->rmp; /* cache ptr to request packet */ 413 rpl = &oldconn->rmp; /* cache ptr to reply packet */ 414 415 if (madeconn) { /* no active connection above; abort */ 416 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 417 retval = 1; 418 goto sendpkt; 419 } 420 421 /* 422 * Make sure Session ID's match. 423 */ 424 if (ntohs(req->r_rrq.rmp_session) != 425 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 426 ntohs(rpl->r_rrpl.rmp_session))) { 427 syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", 428 EnetStr(rconn)); 429 rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; 430 retval = 1; 431 goto sendpkt; 432 } 433 434 /* 435 * If the requester asks for more data than we can fit, 436 * silently clamp the request size down to RMPREADDATA. 437 * 438 * N.B. I do not know if this is "legal", however it seems 439 * to work. This is necessary for bpfwrite() on machines 440 * with MCLBYTES less than 1514. 441 */ 442 if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) 443 req->r_rrq.rmp_size = htons(RMPREADDATA); 444 445 /* 446 * Position read head on file according to info in request packet. 447 */ 448 GETWORD(req->r_rrq.rmp_offset, size); 449 if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) { 450 syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", 451 EnetStr(rconn)); 452 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 453 retval = 1; 454 goto sendpkt; 455 } 456 457 /* 458 * Read data directly into reply packet. 459 */ 460 if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, 461 (int) ntohs(req->r_rrq.rmp_size))) <= 0) { 462 if (size < 0) { 463 syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", 464 EnetStr(rconn)); 465 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 466 } else { 467 rpl->r_rrpl.rmp_retcode = RMP_E_EOF; 468 } 469 retval = 1; 470 goto sendpkt; 471 } 472 473 /* 474 * Set success indication. 475 */ 476 rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; 477 478 sendpkt: 479 /* 480 * Set up assorted fields in reply packet. 481 */ 482 rpl->r_rrpl.rmp_type = RMP_READ_REPL; 483 COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); 484 rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; 485 486 oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ 487 488 retval &= SendPacket(oldconn); /* send packet */ 489 490 if (madeconn) /* clean up after ourself */ 491 FreeConn(oldconn); 492 493 return (retval); 494 } 495 496 /* 497 ** BootDone -- free up memory allocated for a connection. 498 ** 499 ** Parameters: 500 ** rconn - incoming boot complete packet. 501 ** 502 ** Returns: 503 ** 1 on success, 0 on failure. 504 ** 505 ** Side Effects: 506 ** none. 507 */ 508 int 509 BootDone(RMPCONN *rconn) 510 { 511 RMPCONN *oldconn; 512 struct rmp_packet *rpl; 513 514 /* 515 * If we can't find the connection, ignore the request. 516 */ 517 if ((oldconn = FindConn(rconn)) == NULL) { 518 syslog(LOG_ERR, "BootDone: no existing connection (%s)", 519 EnetStr(rconn)); 520 return(0); 521 } 522 523 rpl = &oldconn->rmp; /* cache ptr to RMP packet */ 524 525 /* 526 * Make sure Session ID's match. 527 */ 528 if (ntohs(rconn->rmp.r_rrq.rmp_session) != 529 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 530 ntohs(rpl->r_rrpl.rmp_session))) { 531 syslog(LOG_ERR, "BootDone: bad session id (%s)", 532 EnetStr(rconn)); 533 return(0); 534 } 535 536 RemoveConn(oldconn); /* remove connection */ 537 538 syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); 539 540 return(1); 541 } 542 543 /* 544 ** SendPacket -- send an RMP packet to a remote host. 545 ** 546 ** Parameters: 547 ** rconn - packet to be sent. 548 ** 549 ** Returns: 550 ** 1 on success, 0 on failure. 551 ** 552 ** Side Effects: 553 ** none. 554 */ 555 int 556 SendPacket(RMPCONN *rconn) 557 { 558 /* 559 * Set Ethernet Destination address to Source (BPF and the enet 560 * driver will take care of getting our source address set). 561 */ 562 memmove((char *)&rconn->rmp.hp_hdr.daddr[0], 563 (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN); 564 rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); 565 566 /* 567 * Reverse 802.2/HP Extended Source & Destination Access Pts. 568 */ 569 rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); 570 rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); 571 572 /* 573 * Last time this connection was active. 574 */ 575 (void)gettimeofday(&rconn->tstamp, NULL); 576 577 if (DbgFp != NULL) /* display packet */ 578 DispPkt(rconn,DIR_SENT); 579 580 /* 581 * Send RMP packet to remote host. 582 */ 583 return(BpfWrite(rconn)); 584 } 585