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
ProcessPacket(RMPCONN * rconn,CLIENT * client)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
SendServerID(RMPCONN * rconn)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
SendFileNo(struct rmp_packet * req,RMPCONN * rconn,char * filelist[])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
SendBootRepl(struct rmp_packet * req,RMPCONN * rconn,char * filelist[])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
SendReadRepl(RMPCONN * rconn)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
BootDone(RMPCONN * rconn)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
SendPacket(RMPCONN * rconn)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