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