1 /* $OpenBSD: rbootd.c,v 1.32 2019/06/28 13:32:50 deraadt Exp $ */
2 /* $NetBSD: rbootd.c,v 1.5 1995/10/06 05:12:17 thorpej Exp $ */
3
4 /*
5 * Copyright (c) 1988, 1992 The University of Utah and the Center
6 * for Software Science (CSS).
7 * Copyright (c) 1992, 1993
8 * The Regents of the University of California. All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * the Center for Software Science of the University of Utah Computer
12 * Science Department. CSS requests users of this software to return
13 * to css-dist@cs.utah.edu any improvements that they make and grant
14 * CSS redistribution rights.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * from: @(#)rbootd.c 8.1 (Berkeley) 6/4/93
41 *
42 * From: Utah Hdr: rbootd.c 3.1 92/07/06
43 * Author: Jeff Forys, University of Utah CSS
44 */
45
46 #include <sys/time.h>
47
48 #include <err.h>
49 #include <errno.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <unistd.h>
56 #include <limits.h>
57 #include <pwd.h>
58 #include <poll.h>
59
60 #include "defs.h"
61
62 extern char *__progname; /* from crt0.o */
63
64 volatile sig_atomic_t dodebugoff;
65 volatile sig_atomic_t dodebugon;
66 volatile sig_atomic_t doreconfig;
67
68 void DebugOff(int);
69 void DebugOn(int);
70 void ReConfig(int);
71 void Exit(int);
72
73 void DoDebugOff(void);
74 void DoDebugOn(void);
75 void DoReConfig(void);
76
77 void DoTimeout(void);
78 CLIENT *FindClient(RMPCONN *);
79
80 int
main(int argc,char * argv[])81 main(int argc, char *argv[])
82 {
83 int c, fd;
84 struct passwd *pw;
85 struct pollfd pfd[1];
86
87 closefrom(STDERR_FILENO + 1);
88
89 if ((pw = getpwnam("_rbootd")) == NULL)
90 err(1, "getpwnam");
91
92 while ((c = getopt(argc, argv, "adi:")) != -1)
93 switch (c) {
94 case 'a':
95 BootAny++;
96 break;
97 case 'd':
98 DebugFlg++;
99 break;
100 case 'i':
101 IntfName = optarg;
102 break;
103 }
104 for (; optind < argc; optind++) {
105 if (ConfigFile == NULL)
106 ConfigFile = argv[optind];
107 else {
108 warnx("too many config files (`%s' ignored)",
109 argv[optind]);
110 }
111 }
112
113 if (ConfigFile == NULL) /* use default config file */
114 ConfigFile = DfltConfig;
115
116 if (DebugFlg) {
117 DbgFp = stdout; /* output to stdout */
118
119 (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */
120 (void) signal(SIGUSR2, SIG_IGN);
121 (void) fclose(stderr); /* finished with it */
122 } else {
123 if (daemon(0, 0))
124 err(1, "can't detach from terminal");
125
126 (void) signal(SIGUSR1, DebugOn);
127 (void) signal(SIGUSR2, DebugOff);
128 }
129
130 /*
131 * If no interface was specified, get one now.
132 *
133 * This is convoluted because we want to get the default interface
134 * name for the syslog("restarted") message. If BpfGetIntfName()
135 * runs into an error, it will return a syslog-able error message
136 * (in `errmsg') which will be displayed here.
137 */
138 if (IntfName == NULL) {
139 char *errmsg;
140
141 if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) {
142 /* BpfGetIntfName() returns safe names, using %m */
143 syslog(LOG_ERR, "%s", errmsg);
144 DoExit();
145 }
146 }
147
148 openlog(__progname, LOG_PID, LOG_DAEMON);
149 fd = BpfOpen();
150 syslog(LOG_NOTICE, "restarted (%s)", IntfName);
151
152 (void) signal(SIGHUP, ReConfig);
153 (void) signal(SIGINT, Exit);
154 (void) signal(SIGTERM, Exit);
155
156 gethostname(MyHost, HOST_NAME_MAX+1);
157
158 /*
159 * All boot files are relative to the boot directory, we might
160 * as well chdir() there to make life easier.
161 */
162 if (chdir(BootDir) == -1) {
163 syslog(LOG_ERR, "chdir: %m (%s)", BootDir);
164 DoExit();
165 }
166
167 /*
168 * Initial configuration.
169 */
170 if (GetBootFiles() == 0) /* get list of boot files */
171 DoExit();
172 if (ParseConfig() == 0) /* parse config file */
173 DoExit();
174
175 if (chroot(BootDir) == -1) {
176 syslog(LOG_CRIT, "chroot %s: %m", BootDir);
177 exit(1);
178 }
179 if (chdir("/") == -1) {
180 syslog(LOG_CRIT, "chdir(\"/\"): %m");
181 exit(1);
182 }
183 if (setgroups(1, &pw->pw_gid) ||
184 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
185 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
186 syslog(LOG_CRIT, "can't drop privileges: %m");
187 exit(1);
188 }
189 endpwent();
190
191 /*
192 * Main loop: receive a packet, determine where it came from,
193 * and if we service this host, call routine to handle request.
194 */
195 pfd[0].fd = fd;
196 pfd[0].events = POLLIN;
197 for (;;) {
198 int nsel;
199
200 /*
201 * Check pending actions
202 */
203 if (dodebugoff) {
204 DoDebugOff();
205 dodebugoff = 0;
206 }
207 if (dodebugon) {
208 DoDebugOn();
209 dodebugon = 0;
210 }
211 if (doreconfig) {
212 DoReConfig();
213 doreconfig = 0;
214 }
215
216 nsel = poll(pfd, 1, RmpConns ? RMP_TIMEOUT * 100 : -1);
217
218 if (nsel == -1) {
219 if (errno == EINTR)
220 continue;
221 syslog(LOG_ERR, "poll: %m");
222 DoExit();
223 } else if (nsel == 0) { /* timeout */
224 DoTimeout(); /* clear stale conns */
225 continue;
226 }
227
228 if (pfd[0].revents & POLLIN) {
229 RMPCONN rconn;
230 CLIENT *client;
231 int doread = 1;
232
233 while (BpfRead(&rconn, doread)) {
234 doread = 0;
235
236 if (DbgFp != NULL) /* display packet */
237 DispPkt(&rconn,DIR_RCVD);
238
239 /*
240 * If we do not restrict service, set the
241 * client to NULL (ProcessPacket() handles
242 * this). Otherwise, check that we can
243 * service this host; if not, log a message
244 * and ignore the packet.
245 */
246 if (BootAny) {
247 client = NULL;
248 } else if ((client=FindClient(&rconn))==NULL) {
249 syslog(LOG_INFO,
250 "%s: boot packet ignored",
251 EnetStr(&rconn));
252 continue;
253 }
254
255 ProcessPacket(&rconn,client);
256 }
257 }
258 }
259 }
260
261 /*
262 ** DoTimeout -- Free any connections that have timed out.
263 **
264 ** Parameters:
265 ** None.
266 **
267 ** Returns:
268 ** Nothing.
269 **
270 ** Side Effects:
271 ** - Timed out connections in `RmpConns' will be freed.
272 */
273 void
DoTimeout(void)274 DoTimeout(void)
275 {
276 RMPCONN *rtmp;
277 struct timeval now;
278
279 (void) gettimeofday(&now, NULL);
280
281 /*
282 * For each active connection, if RMP_TIMEOUT seconds have passed
283 * since the last packet was sent, delete the connection.
284 */
285 for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
286 if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) {
287 syslog(LOG_WARNING, "%s: connection timed out (%u)",
288 EnetStr(rtmp), rtmp->rmp.r_type);
289 RemoveConn(rtmp);
290 }
291 }
292
293 /*
294 ** FindClient -- Find client associated with a packet.
295 **
296 ** Parameters:
297 ** rconn - the new packet.
298 **
299 ** Returns:
300 ** Pointer to client info if found, NULL otherwise.
301 **
302 ** Side Effects:
303 ** None.
304 **
305 ** Warnings:
306 ** - This routine must be called with SIGHUP blocked since
307 ** a reconfigure can invalidate the information returned.
308 */
309 CLIENT *
FindClient(RMPCONN * rconn)310 FindClient(RMPCONN *rconn)
311 {
312 CLIENT *ctmp;
313
314 for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next)
315 if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0],
316 (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0)
317 break;
318
319 return(ctmp);
320 }
321
322 /*
323 ** Exit -- Log an error message and exit.
324 **
325 ** Parameters:
326 ** sig - caught signal (or zero if not dying on a signal).
327 **
328 ** Returns:
329 ** Does not return.
330 **
331 ** Side Effects:
332 ** - This process ceases to exist.
333 */
334 void
Exit(int sig)335 Exit(int sig)
336 {
337 struct syslog_data sdata = SYSLOG_DATA_INIT;
338
339 syslog_r(LOG_ERR, &sdata, "going down on signal %d", sig);
340 _exit(1);
341 }
342
343 void
DoExit(void)344 DoExit(void)
345 {
346 syslog(LOG_ERR, "going down on fatal error");
347 exit(1);
348 }
349
350 /*
351 ** ReConfig -- Get new list of boot files and reread config files.
352 **
353 ** Parameters:
354 ** None.
355 **
356 ** Returns:
357 ** Nothing.
358 **
359 ** Side Effects:
360 ** - All active connections are dropped.
361 ** - List of bootable files is changed.
362 ** - List of clients is changed.
363 **
364 ** Warnings:
365 ** - This routine must be called with SIGHUP blocked.
366 */
367 void
ReConfig(int signo)368 ReConfig(int signo)
369 {
370 doreconfig = 1;
371 }
372
373 void
DoReConfig(void)374 DoReConfig(void)
375 {
376 syslog(LOG_NOTICE, "reconfiguring boot server");
377
378 FreeConns();
379
380 if (GetBootFiles() == 0)
381 DoExit();
382
383 if (ParseConfig() == 0)
384 DoExit();
385 }
386
387 /*
388 ** DebugOff -- Turn off debugging.
389 **
390 ** Parameters:
391 ** None.
392 **
393 ** Returns:
394 ** Nothing.
395 **
396 ** Side Effects:
397 ** - Debug file is closed.
398 */
399 void
DebugOff(int signo)400 DebugOff(int signo)
401 {
402 dodebugoff = 1;
403 }
404
405 void
DoDebugOff(void)406 DoDebugOff(void)
407 {
408 if (DbgFp != NULL)
409 (void) fclose(DbgFp);
410
411 DbgFp = NULL;
412 }
413
414 /*
415 ** DebugOn -- Turn on debugging.
416 **
417 ** Parameters:
418 ** None.
419 **
420 ** Returns:
421 ** Nothing.
422 **
423 ** Side Effects:
424 ** - Debug file is opened/truncated if not already opened,
425 ** otherwise do nothing.
426 */
427 void
DebugOn(int signo)428 DebugOn(int signo)
429 {
430 dodebugon = 1;
431 }
432
433 void
DoDebugOn(void)434 DoDebugOn(void)
435 {
436 if (DbgFp == NULL) {
437 if ((DbgFp = fopen(DbgFile, "w")) == NULL)
438 syslog(LOG_ERR, "can't open debug file (%s)", DbgFile);
439 }
440 }
441