Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/rbootd/rmpproto.c
34856 views
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 [email protected] 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
578