chiark / gitweb /
a91a8a531f1c53c177f84e5d6780b16119ec00c7
[become] / src / daemon.c
1 /* -*-c-*-
2  *
3  * $Id: daemon.c,v 1.17 2004/04/08 01:36:20 mdw Exp $
4  *
5  * Running a `become' daemon
6  *
7  * (c) 1998 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 /*----- Header files ------------------------------------------------------*/
30
31 /* --- ANSI headers --- */
32
33 #include <errno.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 /* --- Unix headers --- */
40
41 #include <sys/types.h>
42 #include <sys/time.h>
43 #include <sys/socket.h>
44
45 #include <netinet/in.h>
46
47 #include <arpa/inet.h>
48
49 #include <netdb.h>
50 #include <syslog.h>
51 #include <unistd.h>
52
53 /* --- mLib headers --- */
54
55 #include <mLib/fwatch.h>
56 #include <mLib/quis.h>
57 #include <mLib/report.h>
58 #include <mLib/sel.h>
59 #include <mLib/sig.h>
60 #include <mLib/sym.h>
61 #include <mLib/trace.h>
62
63 /* --- Catacomb headers --- */
64
65 #include <catacomb/buf.h>
66 #include <catacomb/dsa.h>
67 #include <catacomb/key.h>
68 #include <catacomb/mp.h>
69 #include <catacomb/noise.h>
70 #include <catacomb/paranoia.h>
71 #include <catacomb/rand.h>
72 #include <catacomb/sha.h>
73
74 /* --- Local headers --- */
75
76 #include "become.h"
77 #include "config.h"
78 #include "daemon.h"
79 #include "lexer.h"
80 #include "name.h"
81 #include "netg.h"
82 #include "parse.h"
83 #include "rule.h"
84 #include "userdb.h"
85
86 /*----- Arbitrary constants -----------------------------------------------*/
87
88 #define daemon__awakeEvery (10)         /* Awaken this often to rescan */
89
90 /*----- Static variables --------------------------------------------------*/
91
92 static int daemon__port = -1;           /* No particular port yet */
93 static fwatch daemon__cwatch, daemon__kwatch; /* Watching key/config files */
94 static sel_timer daemon__timer;         /* Timer for reading */
95 static sel_state daemon__sel;           /* Select context */
96 static sel_file daemon__listen;         /* Listening socket selector */
97 static const char *daemon__config;      /* Configuration file for daemon */
98 static const char *daemon__keyfile;     /* Keyring file for daemon */
99 static dsa_priv daemon__key;            /* The key data */
100
101 /*----- Main code ---------------------------------------------------------*/
102
103 /* --- @daemon_usePort@ --- *
104  *
105  * Arguments:   @int port@ = port to use, please
106  *
107  * Returns:     ---
108  *
109  * Use:         Instructs the daemon to listen to the given port.
110  */
111
112 void daemon_usePort(int port)
113 {
114   daemon__port = port;
115 }
116
117 /* --- @daemon__moan@ --- *
118  *
119  * Arguments:   @const char *f@ = offending file name
120  *              @int line@ = offending line of the file
121  *              @const char *msg@ = message
122  *              @void *p@ = ignored
123  *
124  * Returns:     ---
125  *
126  * Use:         Reports an error message about a key file.
127  */
128
129 static void daemon__moan(const char *f, int line, const char *msg, void *p)
130 {
131   syslog(LOG_ERR, "key file error: %s: %d: %s", f, line, msg);
132   T( trace(TRACE_DAEMON, "daemon: key file error: %s: %d: %s",
133            f, line, msg); )
134 }
135  
136 /* --- @daemon_readKey@ --- *
137  *
138  * Arguments:   @const char *kf@ = pointer to key file name to use
139  *
140  * Returns:     ---
141  *
142  * Use:         Loads the private key from the key file.
143  */
144
145 void daemon_readKey(const char *kf)
146 {
147   key_packstruct kps[DSA_PRIVFETCHSZ];
148   key_packdef *kp;
149   key_file f;
150   key *k;
151   int err;
152
153   if (daemon__keyfile)
154     return;
155   T( trace(TRACE_DAEMON, "daemon: reading key from `%s'", kf); )
156   if (key_open(&f, kf, KOPEN_READ, daemon__moan, 0))
157     return;
158   kp = key_fetchinit(dsa_privfetch, kps, &daemon__key);
159   if ((k = key_bytype(&f, "become-dsa")) == 0)
160     err = KERR_NOTFOUND;
161   else
162     err = key_fetch(kp, k);
163   if (err)
164     syslog(LOG_ERR, "couldn't load key: %s", key_strerror(err));
165   else {
166     mp_copy(daemon__key.dp.p);
167     mp_copy(daemon__key.dp.q);
168     mp_copy(daemon__key.dp.g);
169     mp_copy(daemon__key.x);
170     mp_copy(daemon__key.y);
171     daemon__keyfile = kf;
172   }
173   key_fetchdone(kp);
174   key_close(&f);
175 }
176
177 /* --- @daemon__readConfig@ --- *
178  *
179  * Arguments:   @const char *cf@ = pointer to configuration file to use
180  *
181  * Returns:     Zero if it worked, nonzero if it hurt...
182  *
183  * Use:         Reads the configuration file, and other things.
184  */
185
186 static int daemon__readConfig(const char *cf)
187 {
188   FILE *fp;
189
190   daemon__keyfile = 0;
191   if ((fp = fopen(cf, "r")) == 0)
192     return (-1);
193   lexer_scan(fp);
194   parse();
195   fclose(fp);
196   if (!daemon__keyfile)
197     daemon_readKey(file_KEY);
198   T( trace(TRACE_DAEMON, "daemon: read config file"); )
199   return (0);
200 }
201
202 /* --- @daemon__read@ --- *
203  *
204  * Arguments:   @int fd@ = socket handle
205  *              @unsigned mode@ = ignored
206  *              @void *p@ = ignored
207  *
208  * Returns:     ---
209  *
210  * Use:         Examines a buffer, and returns a response.
211  */
212
213 void daemon__read(int fd, unsigned mode, void *p)
214 {
215   unsigned char buff[65536];            /* Buffer for incoming packets */
216   struct sockaddr_in sin;               /* Address of packet sender */
217   char sender[64];                      /* Sender's hostname (resolved) */
218   octet h[SHA_HASHSZ];                  /* Hash of the transmission buffer */
219   sha_ctx hc;                           /* Hashing context */
220   request rq;                           /* Request buffer for verification */
221   ssize_t sz;                           /* Length of incoming message */
222   socklen_t slen;                       /* Length of incoming address */
223   uint32 u;                             /* Scratch integer */
224   uint16 ul;                            /* And another */
225   struct hostent *he;                   /* Resolve structure */
226   mp *m, *k, *r, *s;                    /* Integers for signing */
227   int ans;                              /* Answer from the check */
228   buf b;                                /* Buffer for parsing request */
229
230   /* --- Kick some randomness in the pot --- */
231
232   noise_timer(RAND_GLOBAL);
233
234   /* --- Read the message --- */
235
236   slen = sizeof(sin);
237   if ((sz = recvfrom(fd, (char *)buff, sizeof(buff), 0,
238                      (struct sockaddr *)&sin, &slen)) < 0) {
239     T( trace(TRACE_DAEMON, "daemon: error reading packet: %s",
240              strerror(errno)); )
241     syslog(LOG_INFO, "duff packet received: %e");
242     return;
243   }
244
245   /* --- Resolve the host name --- */
246
247   he = gethostbyaddr((char *)&sin.sin_addr, sizeof(sin.sin_addr), AF_INET);
248   sender[0] = 0;
249   strncat(sender, he ? he->h_name : inet_ntoa(sin.sin_addr),
250           sizeof(sender) - 1);
251   syslog(LOG_DEBUG, "packet received from %s", sender);
252   T( trace(TRACE_DAEMON, "daemon: received request from %s", sender); )
253
254   /* --- Sanity check --- */
255
256   if (!daemon__keyfile) {
257     syslog(LOG_NOTICE, "no key file: ignoring request");
258     T( trace(TRACE_DAEMON, "daemon: no key file: ignoring request"); )
259     return;
260   }
261
262   /* --- Unpack the block --- */
263
264   rq.host = sin.sin_addr;
265   buf_init(&b, buff, sz);
266   if (buf_ensure(&b, SHA_HASHSZ)) goto fail;  BSTEP(&b, SHA_HASHSZ);
267   if (buf_getu32(&b, &u)) goto fail;  rq.from = u;
268   if (buf_getu32(&b, &u)) goto fail;  rq.to = u;
269   if (buf_getu16(&b, &ul) || buf_ensure(&b, ul) || ul >= sizeof(rq.cmd))
270     goto fail;
271   memcpy(rq.cmd, BCUR(&b), ul);
272   rq.cmd[ul] = 0;
273   BSTEP(&b, ul);
274   if (BLEFT(&b)) goto fail;
275
276   /* --- Hash the request block --- */
277
278   sha_init(&hc);
279   sha_hash(&hc, buff, sz);
280   sha_done(&hc, h);
281
282   /* --- Build a reply block --- */
283
284   ans = 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, ans ? "granted" : "denied");
287   buf_init(&b, buff, sizeof(buff));
288   if (buf_put(&b, h, sizeof(h)) || buf_putbyte(&b, ans))
289     goto fail;
290
291   /* --- Sign the reply block --- */
292
293   sha_init(&hc);
294   sha_hash(&hc, BBASE(&b), BLEN(&b));
295   sha_done(&hc, h);
296   m = mp_loadb(MP_NEW, h, sizeof(h));
297   rand_get(RAND_GLOBAL, h, sizeof(h));
298   k = mp_loadb(MP_NEWSEC, h, sizeof(h));
299   r = s = MP_NEW;
300   dsa_mksig(&daemon__key.dp, daemon__key.x, m, k, &r, &s);
301   buf_putmp(&b, r);
302   buf_putmp(&b, s);
303   mp_drop(m);
304   mp_drop(k);
305   mp_drop(r);
306   mp_drop(s);
307   if (BBAD(&b))
308     goto fail;
309
310   /* --- Send the reply off --- */
311
312   sendto(fd, BBASE(&b), BLEN(&b), 0, (struct sockaddr *)&sin, sizeof(sin));
313   T( trace(TRACE_DAEMON, "daemon: reply sent"); )
314   return;
315
316 fail:
317   syslog(LOG_ERR, "couldn't respond to query");
318   T( trace(TRACE_DAEMON, "daemon: failed to answer query"); )  
319 }
320
321 /* --- @daemon__die@ --- *
322  *
323  * Arguments:   @int n@ = signal number
324  *              @void *p@ = ignored
325  *
326  * Returns:     Doesn't.
327  *
328  * Use:         Exits the daemon.
329  */
330
331 static void daemon__die(int n, void *p)
332 {
333   T( trace(TRACE_DAEMON, "daemon: killed by signal %i", n); )
334   syslog(LOG_NOTICE, "killed by signal type %i", n);
335   remove(file_PID);
336   exit(0);
337 }
338
339 /* --- @daemon__setTimer@ --- *
340  *
341  * Arguments:   ---
342  *
343  * Returns:     ---
344  *
345  * Use:         Sets the interval timer up.
346  */
347
348 static void daemon__wakeUp(struct timeval *tv, void *p);
349
350 static void daemon__setTimer(void)
351 {
352   struct timeval tv;
353
354   gettimeofday(&tv, 0);
355   tv.tv_sec += daemon__awakeEvery;
356   sel_addtimer(&daemon__sel, &daemon__timer, &tv, daemon__wakeUp, 0);
357 }
358
359 /* --- @daemon__rescan@ --- *
360  *
361  * Arguments:   @int n@ = signal number
362  *              @void *p@ = ignored
363  *
364  * Returns:     ---
365  *
366  * Use:         Forces a rescan of the daemon's configuration.
367  */
368
369 static void daemon__rescan(int n, void *p)
370 {
371   syslog(LOG_INFO, "rescanning configuration file");
372   name_end();
373   rule_end();
374   netg_end();
375   userdb_end();
376   dsa_privfree(&daemon__key);
377   userdb_init();
378   userdb_local();
379   userdb_yp();
380   netg_init();
381   rule_init();
382   name_init();
383   if (daemon__readConfig(daemon__config))
384     syslog(LOG_ERR, "error reading configuration file");
385   sel_rmtimer(&daemon__timer);
386   daemon__setTimer();
387   fwatch_update(&daemon__cwatch, daemon__config);
388   fwatch_update(&daemon__kwatch, daemon__keyfile);
389 }
390
391 /* --- @daemon__wakeUp@ --- *
392  *
393  * Arguments:   @struct timeval *tv@ = ignored
394  *              @void *p@ = ignored
395  *
396  * Returns:     ---
397  *
398  * Use:         Wakes up periodically to check the configuration file.
399  */
400
401 static void daemon__wakeUp(struct timeval *tv, void *p)
402 {
403   T( trace(TRACE_DAEMON, "daemon: interval timer"); )
404   rand_seed(RAND_GLOBAL, 160);
405   daemon__setTimer();
406   if (fwatch_update(&daemon__cwatch, daemon__config))
407     daemon__rescan(0, 0);
408   else if (fwatch_update(&daemon__kwatch, daemon__keyfile)) {
409     const char *kf = daemon__keyfile;
410     daemon__keyfile = 0;
411     daemon_readKey(kf);
412   }
413 }
414
415 /* --- @daemon_init@ --- *
416  *
417  * Arguments:   @const char *cf@ = pointer to name of configuration file
418  *              @int port@ = port to listen to, or %$-1$% for default
419  *              @unsigned f@ = various flags
420  *
421  * Returns:     Never.
422  *
423  * Use:         Starts `become' up in daemon mode.
424  */
425
426 void daemon_init(const char *cf, int port, unsigned f)
427 {
428   int s;
429   int i;
430
431   static struct sigvec {
432     int sig;
433     void (*proc)(int n, void *p);
434     sig s;
435   } sigs[] = {
436     { SIGHUP, daemon__rescan },
437     { SIGINT, daemon__die },
438     { SIGTERM, daemon__die },
439     { SIGQUIT, daemon__die },
440     { 0, 0 }
441   };
442
443   /* --- Remove my root privileges --- *
444    *
445    * Just in case there's anything dodgy in my configuration file, or the
446    * user wants me to start on a funny port.
447    */
448
449   setuid(getuid());
450
451   /* --- Initialize the random number generator --- */
452
453   rand_noisesrc(RAND_GLOBAL, &noise_source);
454   rand_seed(RAND_GLOBAL, 160);
455
456   /* --- Initialise bits of the program --- */
457
458   daemon__config = cf;
459   daemon__port = port;
460   sel_init(&daemon__sel);
461   sig_init(&daemon__sel);
462   userdb_init();
463   userdb_local();
464   userdb_yp();
465   netg_init();
466   name_init();
467   rule_init();
468   openlog(quis(), 0, LOG_DAEMON);
469   syslog(LOG_NOTICE, "starting up");
470
471   if (daemon__readConfig(daemon__config))
472     die(1, "couldn't read configuration file");
473   fwatch_init(&daemon__cwatch, daemon__config);
474   fwatch_init(&daemon__kwatch, daemon__keyfile);
475
476   /* --- Decide on a port to use --- *
477    *
478    * If I don't have a port yet (e.g., from the configuration file) then
479    * look it up in /etc/services under whatever name I was started as.
480    */
481
482   if (daemon__port == 0) {
483     struct servent *se = getservbyname(quis(), "udp");
484     if (se)
485       daemon__port = se->s_port;
486     else
487       daemon__port = htons(SERVER_PORT);
488   }
489
490   /* --- Now set up a socket --- */
491
492   {
493     struct sockaddr_in sin;
494
495     if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
496       die(1, "couldn't create socket: %s", strerror(errno));
497     sin.sin_family = AF_INET;
498     sin.sin_port = daemon__port;
499     sin.sin_addr.s_addr = htonl(INADDR_ANY);
500     if (bind(s, (struct sockaddr *)&sin, sizeof(sin))) {
501       die(1, "couldn't bind socket to port %i: %s",
502           ntohs(daemon__port), strerror(errno));
503     }
504   }
505
506   /* --- Fork off into the sunset --- */
507
508   if (!(f & df_nofork)) {
509     int pid = fork();
510     FILE *fp;
511
512     /* --- Make a background process --- */
513
514     if (pid == -1)
515       die(1, "couldn't fork daemon: %s", strerror(errno));
516     else if (pid != 0)
517       return;
518
519     /* --- Disconnect from the terminal --- */
520
521     setsid();
522
523     /* --- Write my process id to a file --- */
524
525     if ((fp = fopen(file_PID, "w")) != 0) {
526       fprintf(fp, "%lu\n", (unsigned long)getpid());
527       fclose(fp);
528     }
529     T( trace(TRACE_DAEMON, "daemon: forked to pid %li", (long)getpid()); )
530   }
531
532   /* --- Set signal handlers --- */
533
534   for (i = 0; sigs[i].proc; i++)
535     sig_add(&sigs[i].s, sigs[i].sig, sigs[i].proc, 0);
536
537   /* --- Set the timer for rescanning the file --- */
538
539   daemon__setTimer();
540
541   /* --- Watch for input --- */
542
543   sel_initfile(&daemon__sel, &daemon__listen, s, SEL_READ,
544                daemon__read, 0);
545   sel_addfile(&daemon__listen);
546
547   /* --- Now wait for something exciting to happen --- */
548
549   for (;;) {
550     if (sel_select(&daemon__sel)) {
551       if (errno == EINTR || errno == EAGAIN)
552         continue;
553       syslog(LOG_ERR, "error from select: %s", strerror(errno));
554       exit(1);
555     }
556   }
557 }
558
559 /*----- That's all, folks -------------------------------------------------*/