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