New file: simplifies the job of getting a newly checked-out source tree
[become] / src / daemon.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
8702965d 3 * $Id: daemon.c,v 1.10 1998/04/23 13:23:09 mdw Exp $
c4f2d992 4 *
5 * Running a `become' daemon
6 *
c758e654 7 * (c) 1998 EBI
c4f2d992 8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 11 *
12 * This file is part of `become'
13 *
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: daemon.c,v $
8702965d 32 * Revision 1.10 1998/04/23 13:23:09 mdw
33 * Support new interface to configuration file parser.
34 *
c758e654 35 * Revision 1.9 1998/01/12 16:45:59 mdw
36 * Fix copyright date.
37 *
9e5602f0 38 * Revision 1.8 1997/09/26 09:14:58 mdw
39 * Merged blowfish branch into trunk.
40 *
41 * Revision 1.7.2.1 1997/09/26 09:08:05 mdw
42 * Use the Blowfish encryption algorithm instead of IDEA. This is partly
43 * because I prefer Blowfish (without any particularly strong evidence) but
44 * mainly because IDEA is patented and Blowfish isn't.
45 *
0c6c5a97 46 * Revision 1.7 1997/09/17 10:23:23 mdw
47 * Fix a typo. Port numbers are in network order now, so don't change them.
48 *
f3debbd8 49 * Revision 1.6 1997/09/09 18:17:06 mdw
50 * Allow default port to be given as a service name or port number.
51 *
52 * Revision 1.5 1997/08/20 16:17:10 mdw
ffdb9474 53 * More sensible restart routine: `_reinit' functions replaced by `_end' and
54 * `_init' functions.
55 *
a63eaefc 56 * Revision 1.4 1997/08/07 10:00:37 mdw
57 * (Log entry for previous version is bogus.) Read netgroups database.
58 * Give up privileges permanently on startup.
607937a4 59 *
03f996bd 60 * Revision 1.2 1997/08/04 10:24:21 mdw
61 * Sources placed under CVS control.
62 *
63 * Revision 1.1 1997/07/21 13:47:50 mdw
c4f2d992 64 * Initial revision
65 *
66 */
67
68/*----- Header files ------------------------------------------------------*/
69
70/* --- ANSI headers --- */
71
72#include <errno.h>
73#include <signal.h>
74#include <setjmp.h>
75#include <stdio.h>
76#include <stdlib.h>
77#include <string.h>
78
79/* --- Unix headers --- */
80
81#include <sys/types.h>
82#include <sys/time.h>
83#include <sys/socket.h>
84
85#include <netinet/in.h>
86
87#include <arpa/inet.h>
88
89#include <netdb.h>
90#include <syslog.h>
91#include <unistd.h>
92
93/* --- Local headers --- */
94
95#include "become.h"
9e5602f0 96#include "blowfish.h"
c4f2d992 97#include "config.h"
98#include "crypt.h"
99#include "daemon.h"
c4f2d992 100#include "lexer.h"
101#include "name.h"
607937a4 102#include "netg.h"
c4f2d992 103#include "parser.h"
104#include "rule.h"
105#include "tx.h"
106#include "userdb.h"
107#include "utils.h"
108
109/*----- Arbitrary constants -----------------------------------------------*/
110
607937a4 111#define daemon__awakeEvery (30 * 60) /* Awaken this often to rescan */
c4f2d992 112
113/*----- Static variables --------------------------------------------------*/
114
115static int daemon__running = 0; /* Am I running as a daemon? */
116static int daemon__port = -1; /* No particular port yet */
117static volatile sig_atomic_t daemon__rescan = 0; /* Rescan as soon as poss */
118#define daemon__signum daemon__rescan /* Alias for readbility */
119static int daemon__readKey = 0; /* Have I read a key? */
9e5602f0 120static unsigned char daemon__key[BLOWFISH_KEYSIZE]; /* Encryption key */
c4f2d992 121static jmp_buf daemon__dieBuf; /* Jump here to kill the daemon */
122
123/*----- Main code ---------------------------------------------------------*/
124
125/* --- @daemon_usePort@ --- *
126 *
127 * Arguments: @int port@ = port to use, please
128 *
129 * Returns: ---
130 *
131 * Use: Instructs the daemon to listen to the given port.
132 */
133
134void daemon_usePort(int port)
135{
136 daemon__port = port;
137}
138
139/* --- @daemon_readKey@ --- *
140 *
141 * Arguments: @const char *kf@ = name of file containing key
142 *
143 * Returns: ---
144 *
145 * Use: Instructs the daemon to read the named key file.
146 */
147
148void daemon_readKey(const char *kf)
149{
150 FILE *fp;
151
152 if (!daemon__running)
153 return;
154
155 if ((fp = fopen(kf, "r")) == 0) {
156 syslog(LOG_WARNING, "couldn't read key file: %e");
157 return;
158 }
159 tx_getBits(daemon__key, 128, fp);
160 fclose(fp);
161 daemon__readKey = 1;
162 return;
163}
164
165/* --- @daemon__readConfig@ --- *
166 *
167 * Arguments: @const char *cf@ = pointer to configuration file to use
168 *
169 * Returns: Zero if it worked, nonzero if it hurt...
170 *
171 * Use: Reads the configuration file, and other things.
172 */
173
174static int daemon__readConfig(const char *cf)
175{
176 FILE *fp;
177
178 daemon__readKey = 0;
179 if ((fp = fopen(cf, "r")) == 0)
180 return (-1);
181 lexer_scan(fp);
8702965d 182 parse();
c4f2d992 183 fclose(fp);
184 if (!daemon__readKey)
185 daemon_readKey(file_KEY);
186 daemon__rescan = 0;
03f996bd 187 T( trace(TRACE_DAEMON, "daemon: read config file"); )
c4f2d992 188 return (0);
189}
190
191/* --- @daemon__restart@ --- *
192 *
193 * Arguments: @int sig@ = the signal number
194 *
195 * Returns: ---
196 *
197 * Use: Handles signals. Causes the configuration file to be reread.
198 */
199
200static void daemon__restart(int sig)
201{
202 daemon__rescan = 1;
203 signal(sig, daemon__restart);
204}
205
206/* --- @daemon__die@ --- *
207 *
208 * Arguments: @int sig@ = the signal number
209 *
210 * Returns: via @longjmp@
211 *
212 * Use: Handles other signals. Causes the daemon to die.
213 */
214
215static void daemon__die(int sig)
216{
217 daemon__signum = sig;
218 longjmp(daemon__dieBuf, 1);
219}
220
221/* --- @daemon__read@ --- *
222 *
223 * Arguments: @int fd@ = socket handle
224 *
225 * Returns: ---
226 *
227 * Use: Examines a buffer, and returns a response.
228 */
229
230void daemon__read(int fd)
231{
232 unsigned char buff[65536]; /* Buffer for incoming packets */
233 unsigned char rpl[crp_size]; /* Buffer for outgoing replies */
234 struct sockaddr_in sin; /* Address of packet sender */
235 char sender[64]; /* Sender's hostname (resolved) */
9e5602f0 236 unsigned char sk[BLOWFISH_KEYSIZE]; /* Session key for reply */
c4f2d992 237 request rq; /* Request buffer for verification */
238
239 /* --- Read the message --- */
240
241 {
242 int slen = sizeof(sin);
243
244 if (recvfrom(fd, (char *)buff, sizeof(buff), 0,
245 (struct sockaddr *)&sin, &slen) < 0) {
03f996bd 246 T( trace(TRACE_DAEMON, "daemon: error reading packet: %s",
247 strerror(errno)); )
c4f2d992 248 syslog(LOG_INFO, "duff packet received: %e");
249 return;
250 }
251 }
252
253 /* --- Resolve the host name --- */
254
255 {
256 struct hostent *he = gethostbyaddr((char *)&sin.sin_addr,
257 sizeof(sin.sin_addr),
258 AF_INET);
259 sender[0] = 0;
260 strncat(sender,
261 he ? he->h_name : inet_ntoa(sin.sin_addr),
262 sizeof(sender));
263 syslog(LOG_DEBUG, "packet received from %s", sender);
03f996bd 264 T( trace(TRACE_DAEMON, "daemon: received request from %s", sender); )
c4f2d992 265 }
266
267 /* --- Unpack the block --- */
268
269 if (crypt_unpackRequest(&rq, buff, daemon__key, sk, rpl) == 0) {
270 burn(buff);
03f996bd 271 T( trace(TRACE_DAEMON, "daemon: received corrupt or invalid request"); )
c4f2d992 272 syslog(LOG_INFO, "packet from %s rejected", sender);
273 return;
274 }
275 burn(buff);
276
277 /* --- Fill in the sender's address in the request block --- */
278
279 rq.host = sin.sin_addr;
280
281 /* --- Build a reply block --- */
282
283 {
284 int answer = rule_check(&rq);
285 syslog(LOG_INFO, "request from %s for %i to become %i to run %s %s",
286 sender, rq.from, rq.to, rq.cmd, answer ? "granted" : "denied");
287 crypt_packReply(rpl, sk, answer);
288 burn(sk);
289 }
290
291 /* --- Send the reply off --- */
292
293 sendto(fd, (char *)rpl, crp_size, 0, (struct sockaddr *)&sin, sizeof(sin));
03f996bd 294 T( trace(TRACE_DAEMON, "daemon: reply sent"); )
c4f2d992 295 burn(rpl);
296}
297
298/* --- @daemon_init@ --- *
299 *
300 * Arguments: @const char *cf@ = pointer to name of configuration file
301 * @int port@ = port to listen to, or %$-1$% for default
302 *
303 * Returns: Never.
304 *
305 * Use: Starts `become' up in daemon mode.
306 */
307
308void daemon_init(const char *cf, int port)
309{
310 int s;
311
312 /* --- Remove my root privileges --- *
313 *
314 * Just in case there's anything dodgy in my configuration file, or the
315 * user wants me to start on a funny port.
316 */
317
607937a4 318 setuid(getuid());
c4f2d992 319
320 /* --- Initialise bits of the program --- */
321
322 daemon__running = 1;
323 daemon__port = port;
324 userdb_init();
325 userdb_local();
326 userdb_yp();
607937a4 327 netg_init();
c4f2d992 328 name_init();
329 rule_init();
330 openlog(quis(), 0, LOG_DAEMON);
331 syslog(LOG_NOTICE, "starting up");
332
333 if (daemon__readConfig(cf))
334 die("couldn't read configuration file");
335
336 /* --- Decide on a port to use --- *
337 *
338 * If I don't have a port yet (e.g., from the configuration file) then
339 * look it up in /etc/services under whatever name I was started as.
340 */
341
0c6c5a97 342 if (daemon__port == 0) {
c4f2d992 343 struct servent *se = getservbyname(quis(), "udp");
344 if (!se)
9e5602f0 345 die("no idea which port to listen to");
f3debbd8 346 daemon__port = se->s_port;
c4f2d992 347 }
348
349 /* --- Now set up a socket --- */
350
351 {
352 struct sockaddr_in sin;
353
354 if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
355 die("couldn't create socket: %s", strerror(errno));
356 sin.sin_family = AF_INET;
0c6c5a97 357 sin.sin_port = daemon__port;
c4f2d992 358 sin.sin_addr.s_addr = htonl(INADDR_ANY);
0c6c5a97 359 if (bind(s, (struct sockaddr *)&sin, sizeof(sin))) {
360 die("couldn't bind socket to port %i: %s",
361 ntohs(daemon__port), strerror(errno));
362 }
c4f2d992 363 }
364
365 /* --- Fork off into the sunset --- */
366
367#ifdef NDEBUG
368 {
369 int pid = fork();
370 FILE *fp;
371
372 /* --- Make a background process --- */
373
374 if (pid == -1)
375 die("couldn't fork daemon: %s", strerror(errno));
376 else if (pid != 0)
377 return;
378
379 /* --- Disconnect from the terminal --- */
380
381 setsid();
382
383 /* --- Write my process id to a file --- */
384
385 if ((fp = fopen(file_PID, "w")) != 0) {
386 fprintf(fp, "%lu\n", (unsigned long)getpid());
387 fclose(fp);
388 }
03f996bd 389 T( trace(TRACE_DAEMON, "daemon: forked to pid %li", (long)getpid()); )
c4f2d992 390 }
391#endif
392
393 /* --- Program in daemon death mode --- */
394
395 if (setjmp(daemon__dieBuf)) {
03f996bd 396#ifdef TRACING
397 if (daemon__signum == SIGQUIT && tracing() & TRACE_RULE) {
398 T( rule_dump(); )
399 signal(SIGQUIT, daemon__die);
400 } else
401#endif
402 {
403 T( trace(TRACE_DAEMON, "daemon: killed by signal %i",
404 daemon__signum); )
405 syslog(LOG_NOTICE, "killed by signal type %i", daemon__signum);
406 remove(file_PID);
407 exit(0);
408 }
409 } else {
c4f2d992 410
03f996bd 411 /* --- Set signal handlers --- */
c4f2d992 412
03f996bd 413 signal(SIGHUP, daemon__restart);
414 signal(SIGQUIT, daemon__die);
415 signal(SIGINT, daemon__die);
416 signal(SIGTERM, daemon__die);
417 signal(SIGSEGV, daemon__die);
418 signal(SIGFPE, daemon__die);
419 signal(SIGBUS, daemon__die);
420 }
c4f2d992 421
422 /* --- Now wait for something exciting to happen --- *
423 *
424 * Actually, every so often (5 minutes, perhaps) I need to wake up and
425 * rescan the users to see whether they've changed. Time to play with
426 * @select@.
427 */
428
429 {
430 time_t when;
431
432 /* --- Find when I am, and thus when I need to be awoken again --- */
433
434 when = time(0) + daemon__awakeEvery;
435
436 for (;;) {
437 fd_set fds;
438 int i;
439
440 /* --- Set up the file descriptor tables --- */
441
442 FD_ZERO(&fds);
443 FD_SET(s, &fds);
444
445 /* --- Now wait for something interesting --- */
446
03f996bd 447 T( trace(TRACE_DAEMON, "daemon: waiting for requests"); )
c4f2d992 448 i = select(FD_SETSIZE, &fds, 0, 0, 0);
449
450 /* --- Now, see if I need to rescan the config --- */
451
452 if (daemon__rescan || time(0) - when > 0) {
453 daemon__rescan = 0;
454 syslog(LOG_INFO, "rescanning configuration file");
ffdb9474 455 name_end();
456 rule_end();
457 netg_end();
458 userdb_end();
459 userdb_init();
c4f2d992 460 userdb_local();
461 userdb_yp();
ffdb9474 462 netg_init();
463 rule_init();
464 name_init();
c4f2d992 465 if (daemon__readConfig(cf))
466 syslog(LOG_ERR, "error reading configuration file");
467 when = time(0) + daemon__awakeEvery;
468 }
469
470 /* --- Read the data from the request --- */
471
472 if (i > 0)
473 daemon__read(s);
474 }
475 }
476}
477
478/*----- That's all, folks -------------------------------------------------*/