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. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 42 * 43 * From: Utah Hdr: rmpproto.c 3.1 92/07/06 44 * Author: Jeff Forys, University of Utah CSS 45 * 46 * @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 47 * $FreeBSD: src/libexec/rbootd/rmpproto.c,v 1.6.2.1 2001/02/18 02:54:11 kris Exp $ 48 */ 49 50 #include <sys/param.h> 51 #include <sys/time.h> 52 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <stdio.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <unistd.h> 59 #include "defs.h" 60 61 /* 62 ** ProcessPacket -- determine packet type and do what's required. 63 ** 64 ** An RMP BOOT packet has been received. Look at the type field 65 ** and process Boot Requests, Read Requests, and Boot Complete 66 ** packets. Any other type will be dropped with a warning msg. 67 ** 68 ** Parameters: 69 ** rconn - the new connection 70 ** client - list of files available to this host 71 ** 72 ** Returns: 73 ** Nothing. 74 ** 75 ** Side Effects: 76 ** - If this is a valid boot request, it will be added to 77 ** the linked list of outstanding requests (RmpConns). 78 ** - If this is a valid boot complete, its associated 79 ** entry in RmpConns will be deleted. 80 ** - Also, unless we run out of memory, a reply will be 81 ** sent to the host that sent the packet. 82 */ 83 void 84 ProcessPacket(RMPCONN *rconn, CLIENT *client) 85 { 86 struct rmp_packet *rmp; 87 RMPCONN *rconnout; 88 89 rmp = &rconn->rmp; /* cache pointer to RMP packet */ 90 91 switch(rmp->r_type) { /* do what we came here to do */ 92 case RMP_BOOT_REQ: /* boot request */ 93 if ((rconnout = NewConn(rconn)) == NULL) 94 return; 95 96 /* 97 * If the Session ID is 0xffff, this is a "probe" 98 * packet and we do not want to add the connection 99 * to the linked list of active connections. There 100 * are two types of probe packets, if the Sequence 101 * Number is 0 they want to know our host name, o/w 102 * they want the name of the file associated with 103 * the number spec'd by the Sequence Number. 104 * 105 * If this is an actual boot request, open the file 106 * and send a reply. If SendBootRepl() does not 107 * return 0, add the connection to the linked list 108 * of active connections, otherwise delete it since 109 * an error was encountered. 110 */ 111 if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { 112 if (WORDZE(rmp->r_brq.rmp_seqno)) 113 (void) SendServerID(rconnout); 114 else 115 (void) SendFileNo(rmp, rconnout, 116 client? client->files: 117 BootFiles); 118 FreeConn(rconnout); 119 } else { 120 if (SendBootRepl(rmp, rconnout, 121 client? client->files: BootFiles)) 122 AddConn(rconnout); 123 else 124 FreeConn(rconnout); 125 } 126 break; 127 128 case RMP_BOOT_REPL: /* boot reply (not valid) */ 129 syslog(LOG_WARNING, "%s: sent a boot reply", 130 EnetStr(rconn)); 131 break; 132 133 case RMP_READ_REQ: /* read request */ 134 /* 135 * Send a portion of the boot file. 136 */ 137 (void) SendReadRepl(rconn); 138 break; 139 140 case RMP_READ_REPL: /* read reply (not valid) */ 141 syslog(LOG_WARNING, "%s: sent a read reply", 142 EnetStr(rconn)); 143 break; 144 145 case RMP_BOOT_DONE: /* boot complete */ 146 /* 147 * Remove the entry from the linked list of active 148 * connections. 149 */ 150 (void) BootDone(rconn); 151 break; 152 153 default: /* unknown RMP packet type */ 154 syslog(LOG_WARNING, "%s: unknown packet type (%u)", 155 EnetStr(rconn), rmp->r_type); 156 } 157 } 158 159 /* 160 ** SendServerID -- send our host name to who ever requested it. 161 ** 162 ** Parameters: 163 ** rconn - the reply packet to be formatted. 164 ** 165 ** Returns: 166 ** 1 on success, 0 on failure. 167 ** 168 ** Side Effects: 169 ** none. 170 */ 171 int 172 SendServerID(RMPCONN *rconn) 173 { 174 struct rmp_packet *rpl; 175 char *src, *dst; 176 u_int8_t *size; 177 178 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 179 180 /* 181 * Set up assorted fields in reply packet. 182 */ 183 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 184 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 185 ZEROWORD(rpl->r_brpl.rmp_seqno); 186 rpl->r_brpl.rmp_session = 0; 187 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 188 189 size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ 190 191 /* 192 * Copy our host name into the reply packet incrementing the 193 * length as we go. Stop at RMP_HOSTLEN or the first dot. 194 */ 195 src = MyHost; 196 dst = (char *) &rpl->r_brpl.rmp_flnm; 197 for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { 198 if (*src == '.' || *src == '\0') 199 break; 200 *dst++ = *src++; 201 } 202 203 rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ 204 205 return(SendPacket(rconn)); /* send packet */ 206 } 207 208 /* 209 ** SendFileNo -- send the name of a bootable file to the requester. 210 ** 211 ** Parameters: 212 ** req - RMP BOOT packet containing the request. 213 ** rconn - the reply packet to be formatted. 214 ** filelist - list of files available to the requester. 215 ** 216 ** Returns: 217 ** 1 on success, 0 on failure. 218 ** 219 ** Side Effects: 220 ** none. 221 */ 222 int 223 SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) 224 { 225 struct rmp_packet *rpl; 226 char *src, *dst; 227 u_int8_t *size; 228 int i; 229 230 GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ 231 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 232 233 /* 234 * Set up assorted fields in reply packet. 235 */ 236 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 237 PUTWORD(i, rpl->r_brpl.rmp_seqno); 238 i--; 239 rpl->r_brpl.rmp_session = 0; 240 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 241 242 size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ 243 *size = 0; /* init length to zero */ 244 245 /* 246 * Copy the file name into the reply packet incrementing the 247 * length as we go. Stop at end of string or when RMPBOOTDATA 248 * characters have been copied. Also, set return code to 249 * indicate success or "no more files". 250 */ 251 if (i < C_MAXFILE && filelist[i] != NULL) { 252 src = filelist[i]; 253 dst = (char *)&rpl->r_brpl.rmp_flnm; 254 for (; *src && *size < RMPBOOTDATA; (*size)++) { 255 if (*src == '\0') 256 break; 257 *dst++ = *src++; 258 } 259 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 260 } else 261 rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; 262 263 rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ 264 265 return(SendPacket(rconn)); /* send packet */ 266 } 267 268 /* 269 ** SendBootRepl -- open boot file and respond to boot request. 270 ** 271 ** Parameters: 272 ** req - RMP BOOT packet containing the request. 273 ** rconn - the reply packet to be formatted. 274 ** filelist - list of files available to the requester. 275 ** 276 ** Returns: 277 ** 1 on success, 0 on failure. 278 ** 279 ** Side Effects: 280 ** none. 281 */ 282 int 283 SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[]) 284 { 285 int retval; 286 char *filename, filepath[RMPBOOTDATA+1]; 287 RMPCONN *oldconn; 288 struct rmp_packet *rpl; 289 char *src, *dst1, *dst2; 290 u_int8_t i; 291 292 /* 293 * If another connection already exists, delete it since we 294 * are obviously starting again. 295 */ 296 if ((oldconn = FindConn(rconn)) != NULL) { 297 syslog(LOG_WARNING, "%s: dropping existing connection", 298 EnetStr(oldconn)); 299 RemoveConn(oldconn); 300 } 301 302 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 303 304 /* 305 * Set up assorted fields in reply packet. 306 */ 307 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 308 COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); 309 rpl->r_brpl.rmp_session = htons(GenSessID()); 310 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 311 rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; 312 313 /* 314 * Copy file name to `filepath' string, and into reply packet. 315 */ 316 src = &req->r_brq.rmp_flnm; 317 dst1 = filepath; 318 dst2 = &rpl->r_brpl.rmp_flnm; 319 for (i = 0; i < req->r_brq.rmp_flnmsize; i++) 320 *dst1++ = *dst2++ = *src++; 321 *dst1 = '\0'; 322 323 /* 324 * If we are booting HP-UX machines, their secondary loader will 325 * ask for files like "/hp-ux". As a security measure, we do not 326 * allow boot files to lay outside the boot directory (unless they 327 * are purposely link'd out. So, make `filename' become the path- 328 * stripped file name and spoof the client into thinking that it 329 * really got what it wanted. 330 */ 331 if ((filename = strrchr(filepath, '/')) != NULL) 332 filename++; 333 else 334 filename = 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 doesnt 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 cant 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