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