chiark / gitweb /
Debianization.
[tripe] / tripe.c
1 /* -*-c-*-
2  *
3  * $Id: tripe.c,v 1.12 2003/11/29 23:49:32 mdw Exp $
4  *
5  * Main program
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE 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  * TrIPE 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 TrIPE; 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: tripe.c,v $
32  * Revision 1.12  2003/11/29 23:49:32  mdw
33  * Debianization.
34  *
35  * Revision 1.11  2003/05/17 11:02:03  mdw
36  * Document the new `-b' option.
37  *
38  * Revision 1.10  2003/05/16 12:09:03  mdw
39  * Allow binding to a chosen address.
40  *
41  * Revision 1.9  2003/04/15 14:11:09  mdw
42  * Rationalize the behaviour of the `-G' and `-U' options.
43  *
44  * Revision 1.8  2002/01/13 17:28:29  mdw
45  * Don't turn tracing on if tracing is turned off (!).
46  *
47  * Revision 1.7  2002/01/13 17:14:05  mdw
48  * Don't include the tracing option in the help if not compiled in.
49  *
50  * Revision 1.6  2001/06/19 22:08:37  mdw
51  * Moved buffers to peer.c.
52  *
53  * Revision 1.5  2001/02/16 21:43:12  mdw
54  * Provide a more helpful usage message.
55  *
56  * Revision 1.4  2001/02/16 21:41:31  mdw
57  * Add a new buffer.
58  *
59  * Revision 1.3  2001/02/04 17:10:40  mdw
60  * Remove a debugging @abort@ call.
61  *
62  * Revision 1.2  2001/02/03 22:33:00  mdw
63  * Stuff more randomness into the pool in the interval timer.
64  *
65  * Revision 1.1  2001/02/03 20:26:37  mdw
66  * Initial checkin.
67  *
68  */
69
70 /*----- Header files ------------------------------------------------------*/
71
72 #include "tripe.h"
73
74 /*----- Global variables --------------------------------------------------*/
75
76 sel_state sel;
77
78 /*----- Static variables --------------------------------------------------*/
79
80 static sel_timer it;
81 #define T_INTERVAL MIN(1)
82
83 /*----- Main code ---------------------------------------------------------*/
84
85 /* --- @interval@ --- *
86  *
87  * Arguments:   @struct timeval *tv@ = time when called
88  *              @void *v@ = boring pointer
89  *
90  * Returns:     ---
91  *
92  * Use:         Called periodically to do housekeeping tasks.
93  */
94
95 void interval(struct timeval *tv, void *v)
96 {
97   struct timeval tvv;
98   T( trace(T_PEER, "peer: interval timer"); )
99   rand_seed(RAND_GLOBAL, HASHSZ);
100   p_interval();
101   tvv = *tv;
102   tvv.tv_sec += T_INTERVAL;
103   sel_addtimer(&sel, &it, &tvv, interval, v);
104 }
105
106 /* --- @main@ --- *
107  *
108  * Arguments:   @int argc@ = number of command line arguments
109  *              @char *argv[]@ = vector of arguments
110  *
111  * Returns:     Zero if OK, nonzero on error.
112  *
113  * Use:         Main program.  Provides a simple VPN.
114  */
115
116 static void usage(FILE *fp)
117 {
118   pquis(fp, "Usage: $ [-D] [-d dir] [-b addr] [-p port]\n\
119         [-U user] [-G group] [-a socket] [-T trace-opts]\n\
120         [-k priv-keyring] [-K pub-keyring] [-t key-tag]\n");
121 }
122
123 static void version(FILE *fp)
124 {
125   pquis(fp, "$, version " VERSION "\n");
126 }
127
128 static void help(FILE *fp)
129 {
130   version(fp);
131   fputc('\n', fp);
132   usage(fp);
133   fputs("\n\
134 Options:\n\
135 \n\
136 -h, --help              Display this help text.\n\
137 -v, --version           Display version number.\n\
138 -u, --usage             Display pointless usage message.\n\
139 \n\
140 -D, --daemon            Run in the background.\n\
141 -d, --directory=DIR     Switch to directory DIR [default " CONFIGDIR "].\n\
142 -b, --bind-address=ADDR Bind UDP socket to this IP ADDR.\n\
143 -p, --port=PORT         Select UDP port to listen to.\n\
144 -U, --setuid=USER       Set uid to USER after initialization.\n\
145 -G, --setgid=GROUP      Set gid to GROUP after initialization.\n\
146 -k, --priv-keyring=FILE Get private key from FILE.\n\
147 -K, --pub-keyring=FILE  Get public keys from FILE.\n\
148 -t, --tag=KEYTAG        Use private key labelled TAG.\n\
149 -a, --admin-socket=FILE Use FILE as the adminstration socket.\n\
150 " T( "\
151 -T, --trace=OPTIONS     Turn on tracing options.\n\
152 " ) "\
153 ", fp);
154 }
155
156 int main(int argc, char *argv[])
157 {
158   const char *kr_priv = "keyring", *kr_pub = "keyring.pub";
159   const char *tag_priv = "tripe-dh";
160   const char *csock = SOCKETDIR "/tripesock";
161   const char *dir = CONFIGDIR;
162   const char *p;
163   unsigned port = 0;
164   struct in_addr baddr = { INADDR_ANY };
165   unsigned f = 0;
166   uid_t u = -1;
167   gid_t g = -1;
168
169 #define f_bogus 1u
170 #define f_daemon 2u
171
172   ego(argv[0]);
173   T( trace_on(stderr, 0); )
174
175   if ((p = getenv("TRIPEDIR")) != 0)
176     dir = p;
177
178   for (;;) {
179     static const struct option opts[] = {
180       { "help",         0,              0,      'h' },
181       { "version",      0,              0,      'v' },
182       { "usage",        0,              0,      'u' },
183
184       { "daemon",       0,              0,      'D' },
185       { "uid",          OPTF_ARGREQ,    0,      'U' },
186       { "setuid",       OPTF_ARGREQ,    0,      'U' },
187       { "gid",          OPTF_ARGREQ,    0,      'G' },
188       { "setgid",       OPTF_ARGREQ,    0,      'G' },
189       { "bind-address", OPTF_ARGREQ,    0,      'b' },
190       { "port",         OPTF_ARGREQ,    0,      'p' },
191       { "directory",    OPTF_ARGREQ,    0,      'd' },
192       { "priv-keyring", OPTF_ARGREQ,    0,      'k' },
193       { "pub-keyring",  OPTF_ARGREQ,    0,      'K' },
194       { "tag",          OPTF_ARGREQ,    0,      't' },
195       { "admin-socket", OPTF_ARGREQ,    0,      'a' },
196 #ifndef NTRACE
197       { "trace",        OPTF_ARGREQ,    0,      'T' },
198 #endif
199
200       { "tunnel",       0,              0,      '0' },
201       { 0,              0,              0,      0 }
202     };
203
204     int i = mdwopt(argc, argv, "hvu DU:G: b:p:d:k:K:t:a:" T("T:"),
205                    opts, 0, 0, 0);
206     if (i < 0)
207       break;
208     switch (i) {
209       case 'h':
210         help(stdout);
211         exit(0);
212       case 'v':
213         version(stdout);
214         exit(0);
215       case 'u':
216         usage(stdout);
217         exit(0);
218
219       case 'D':
220         f |= f_daemon;
221         break;
222       case 'U': {
223         struct passwd *pw;
224         char *p;
225         unsigned long i = strtoul(optarg, &p, 0);
226         if (!*p)
227           pw = getpwuid(i);
228         else
229           pw = getpwnam(optarg);
230         if (!pw)
231           die(EXIT_FAILURE, "user `%s' not found", optarg);
232         u = pw->pw_uid;
233         if (g == -1)
234           g = pw->pw_gid;
235       } break;
236       case 'G': {
237         struct group *gr;
238         char *p;
239         unsigned long i = strtoul(optarg, &p, 0);
240         if (!*p)
241           gr = getgrgid(i);
242         else
243           gr = getgrnam(optarg);
244         if (!gr)
245           die(EXIT_FAILURE, "group `%s' not found", optarg);
246         g = gr->gr_gid;
247       } break;
248
249       case 'b': {
250         struct hostent *h = gethostbyname(optarg);
251         if (!h)
252           die(EXIT_FAILURE, "unknown host name `%s'", optarg);
253         memcpy(&baddr, h->h_addr, sizeof(struct in_addr));
254       } break;
255       case 'p': {
256         char *p;
257         unsigned long i = strtoul(optarg, &p, 0);
258         if (*p) {
259           struct servent *s = getservbyname(optarg, "udp");
260           if (!s)
261             die(EXIT_FAILURE, "unknown service name `%s'", optarg);
262           i = ntohs(s->s_port);
263         }
264         if (i == 0 || i >= 65536)
265           die(EXIT_FAILURE, "bad port number %lu", i);
266         port = i;
267       } break;
268       case 'd':
269         dir = optarg;
270         break;
271       case 'k':
272         kr_priv = optarg;
273         break;
274       case 'K':
275         kr_pub = optarg;
276         break;
277       case 'a':
278         csock = optarg;
279         break;
280       case 't':
281         tag_priv = optarg;
282         break;
283 #ifndef NTRACE
284       case 'T':
285         tr_flags = traceopt(tr_opts, optarg, tr_flags, 0);
286         trace_level(tr_flags);
287         break;
288 #endif
289       case '0': {
290         static const char *tun[] = { "notdef", "unet", "bsd", "linux" };
291         puts(tun[TUN_TYPE]);
292         exit(0);
293       } break;
294       default:
295         f |= f_bogus;
296         break;
297     }
298   }
299
300   if (optind < argc || (f & f_bogus)) {
301     usage(stderr);
302     exit(EXIT_FAILURE);
303   }
304
305   if (chdir(dir)) {
306     die(EXIT_FAILURE, "can't set current directory to `%s': %s",
307         dir, strerror(errno));
308   }
309
310   sel_init(&sel);
311   sig_init(&sel);
312   rand_noisesrc(RAND_GLOBAL, &noise_source);
313   rand_seed(RAND_GLOBAL, RMD160_HASHSZ);
314   signal(SIGPIPE, SIG_IGN);
315   tun_init();
316   p_init(baddr, port);
317   if (!(f & f_daemon))
318     a_create(STDIN_FILENO, STDOUT_FILENO);
319   if (g != (gid_t)-1) {
320     if (setgid(g) || (getuid() == 0 && setgroups(1, &g))) {
321       die(EXIT_FAILURE, "couldn't setgid to %u: %s",
322           (unsigned)g, strerror(errno));
323     }
324   }
325   if (u != (uid_t)-1) {
326     if (setuid(u)) {
327       die(EXIT_FAILURE, "couldn't setuid to %u: %s",
328           (unsigned)u, strerror(errno));
329     }
330   }
331   km_init(kr_priv, kr_pub, tag_priv);
332   a_init(csock);
333   if (f & f_daemon) {
334     if (u_daemon())
335       die(EXIT_FAILURE, "couldn't become a daemon: %s", strerror(errno));
336     a_daemon();
337   }
338
339   {
340     struct timeval tv;
341     tv.tv_sec = time(0) + T_INTERVAL;
342     tv.tv_usec = 0;
343     sel_addtimer(&sel, &it, &tv, interval, 0);
344   }
345
346   {
347     int selerr = 0;
348     for (;;) {
349       if (!sel_select(&sel))
350         selerr = 0;
351       else if (errno != EINTR && errno != EAGAIN) {
352         a_warn("select failed: %s", strerror(errno));
353         selerr++;
354         if (selerr > 8) {
355           a_warn("too many select errors: bailing out");
356           a_quit();
357         }
358       }
359     }
360   }
361
362   return (0);
363 }
364
365 /*----- That's all, folks -------------------------------------------------*/