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