]> mj.ucw.cz Git - moe.git/blob - submit/submitd.c
Combining local and remote operations.
[moe.git] / submit / submitd.c
1 /*
2  *  The Submit Daemon
3  *
4  *  (c) 2007 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef LOCAL_DEBUG
8
9 #include "lib/lib.h"
10 #include "lib/conf.h"
11 #include "lib/getopt.h"
12
13 #include <string.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/wait.h>
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23
24 #include "submitd.h"
25
26 /*** CONFIGURATION ***/
27
28 static byte *log_name;
29 static uns port = 8888;
30 static uns dh_bits = 1024;
31 static uns max_conn = 10;
32 static uns session_timeout;
33 static byte *ca_cert_name = "?";
34 static byte *server_cert_name = "?";
35 static byte *server_key_name = "?";
36 static clist access_rules;
37 static uns trace_tls;
38 uns max_request_size;
39 uns max_attachment_size;
40 uns trace_commands;
41
42 static struct cf_section access_conf = {
43   CF_TYPE(struct access_rule),
44   CF_ITEMS {
45     CF_USER("IP", PTR_TO(struct access_rule, addrmask), &ip_addrmask_type),
46     CF_UNS("Admin", PTR_TO(struct access_rule, allow_admin)),
47     CF_UNS("PlainText", PTR_TO(struct access_rule, plain_text)),
48     CF_UNS("MaxConn", PTR_TO(struct access_rule, max_conn)),
49     CF_END
50   }
51 };
52
53 static struct cf_section submitd_conf = {
54   CF_ITEMS {
55     CF_STRING("LogFile", &log_name),
56     CF_UNS("Port", &port),
57     CF_UNS("DHBits", &dh_bits),
58     CF_UNS("MaxConn", &max_conn),
59     CF_UNS("SessionTimeout", &session_timeout),
60     CF_UNS("MaxRequestSize", &max_request_size),
61     CF_UNS("MaxAttachSize", &max_attachment_size),
62     CF_STRING("CACert", &ca_cert_name),
63     CF_STRING("ServerCert", &server_cert_name),
64     CF_STRING("ServerKey", &server_key_name),
65     CF_LIST("Access", &access_rules, &access_conf),
66     CF_UNS("TraceTLS", &trace_tls),
67     CF_UNS("TraceCommands", &trace_commands),
68     CF_END
69   }
70 };
71
72 /*** CONNECTIONS ***/
73
74 static clist connections;
75 static uns last_conn_id;
76 static uns num_conn;
77
78 static void
79 conn_init(void)
80 {
81   clist_init(&connections);
82 }
83
84 static struct conn *
85 conn_new(void)
86 {
87   struct conn *c = xmalloc_zero(sizeof(*c));
88   c->id = ++last_conn_id;
89   clist_add_tail(&connections, &c->n);
90   num_conn++;
91   return c;
92 }
93
94 static void
95 conn_free(struct conn *c)
96 {
97   xfree(c->ip_string);
98   xfree(c->cert_name);
99   clist_remove(&c->n);
100   num_conn--;
101   xfree(c);
102 }
103
104 static struct access_rule *
105 lookup_rule(u32 ip)
106 {
107   CLIST_FOR_EACH(struct access_rule *, r, access_rules)
108     if (ip_addrmask_match(&r->addrmask, ip))
109       return r;
110   return NULL;
111 }
112
113 static uns
114 conn_count(u32 ip)
115 {
116   uns cnt = 0;
117   CLIST_FOR_EACH(struct conn *, c, connections)
118     if (c->ip == ip)
119       cnt++;
120   return cnt;
121 }
122
123 /*** TLS ***/
124
125 static gnutls_certificate_credentials_t cert_cred;
126 static gnutls_dh_params_t dh_params;
127
128 #define TLS_CHECK(name) if (err < 0) die(#name " failed: %s", gnutls_strerror(err))
129
130 static void
131 tls_init(void)
132 {
133   int err;
134
135   gnutls_global_init();
136   err = gnutls_certificate_allocate_credentials(&cert_cred);
137   TLS_CHECK(gnutls_certificate_allocate_credentials);
138   err = gnutls_certificate_set_x509_trust_file(cert_cred, ca_cert_name, GNUTLS_X509_FMT_PEM);
139   if (!err)
140     die("No CA certificate found");
141   if (err < 0)
142     die("Unable to load X509 trust file: %s", gnutls_strerror(err));
143   err = gnutls_certificate_set_x509_key_file(cert_cred, server_cert_name, server_key_name, GNUTLS_X509_FMT_PEM);
144   if (err < 0)
145     die("Unable to load X509 key file: %s", gnutls_strerror(err));
146
147   err = gnutls_dh_params_init(&dh_params); TLS_CHECK(gnutls_dh_params_init);
148   err = gnutls_dh_params_generate2(dh_params, dh_bits); TLS_CHECK(gnutls_dh_params_generate2);
149   gnutls_certificate_set_dh_params(cert_cred, dh_params);
150 }
151
152 static gnutls_session_t
153 tls_new_session(int sk)
154 {
155   gnutls_session_t s;
156   int err;
157
158   err = gnutls_init(&s, GNUTLS_SERVER); TLS_CHECK(gnutls_init);
159   err = gnutls_set_default_priority(s); TLS_CHECK(gnutls_set_default_priority);                 // FIXME
160   gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, cert_cred);
161   gnutls_certificate_server_set_request(s, GNUTLS_CERT_REQUEST);
162   gnutls_dh_set_prime_bits(s, dh_bits);
163   gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t) sk);
164   return s;
165 }
166
167 static const char *
168 tls_verify_cert(struct conn *c)
169 {
170   gnutls_session_t s = c->tls;
171   uns status, num_certs;
172   int err;
173   gnutls_x509_crt_t cert;
174   const gnutls_datum_t *certs;
175
176   DBG("Verifying peer certificates");
177   err = gnutls_certificate_verify_peers2(s, &status);
178   if (err < 0)
179     return gnutls_strerror(err);
180   DBG("Verify status: %04x", status);
181   if (status & GNUTLS_CERT_INVALID)
182     return "Certificate is invalid";
183   /* XXX: We do not handle revokation. */
184   if (gnutls_certificate_type_get(s) != GNUTLS_CRT_X509)
185     return "Certificate is not X509";
186
187   err = gnutls_x509_crt_init(&cert);
188   if (err < 0)
189     return "gnutls_x509_crt_init() failed";
190   certs = gnutls_certificate_get_peers(s, &num_certs);
191   if (!certs)
192     return "No peer certificate found";
193   DBG("Got certificate list with %d peers", num_certs);
194
195   err = gnutls_x509_crt_import(cert, &certs[0], GNUTLS_X509_FMT_DER);
196   if (err < 0)
197     return "Cannot import certificate";
198   /* XXX: We do not check expiration and activation since the keys are generated for a single contest only anyway. */
199
200   byte dn[256];
201   size_t dn_len = sizeof(dn);
202   err = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &dn_len);
203   if (err < 0)
204     return "Cannot retrieve common name";
205   if (trace_tls)
206     log(L_INFO, "Cert CN: %s", dn);
207   c->cert_name = xstrdup(dn);
208
209   /* Check certificate purpose */
210   byte purp[256];
211   int purpi = 0;
212   do
213     {
214       size_t purp_len = sizeof(purp);
215       uns crit;
216       err = gnutls_x509_crt_get_key_purpose_oid(cert, purpi++, purp, &purp_len, &crit);
217       if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
218         return "Not a client certificate";
219       TLS_CHECK(gnutls_x509_crt_get_key_purpose_oid);
220     }
221   while (strcmp(purp, GNUTLS_KP_TLS_WWW_CLIENT));
222
223   DBG("Verified OK");
224   return NULL;
225 }
226
227 static void
228 tls_log_params(struct conn *c)
229 {
230   if (!trace_tls)
231     return;
232   gnutls_session_t s = c->tls;
233   const char *proto = gnutls_protocol_get_name(gnutls_protocol_get_version(s));
234   const char *kx = gnutls_kx_get_name(gnutls_kx_get(s));
235   const char *cert = gnutls_certificate_type_get_name(gnutls_certificate_type_get(s));
236   const char *comp = gnutls_compression_get_name(gnutls_compression_get(s));
237   const char *cipher = gnutls_cipher_get_name(gnutls_cipher_get(s));
238   const char *mac = gnutls_mac_get_name(gnutls_mac_get(s));
239   log(L_DEBUG, "TLS params: proto=%s kx=%s cert=%s comp=%s cipher=%s mac=%s",
240     proto, kx, cert, comp, cipher, mac);
241 }
242
243 /*** FASTBUFS OVER SOCKETS AND TLS ***/
244
245 void NONRET                             // Fatal protocol violation
246 client_error(char *msg, ...)
247 {
248   va_list args;
249   va_start(args, msg);
250   vlog_msg(L_ERROR_R, msg, args);
251   exit(0);
252 }
253
254 static int
255 sk_fb_refill(struct fastbuf *f)
256 {
257   struct conn *c = SKIP_BACK(struct conn, rx_fb, f);
258   int cnt = read(c->sk, f->buffer, f->bufend - f->buffer);
259   if (cnt < 0)
260     client_error("Read error: %m");
261   f->bptr = f->buffer;
262   f->bstop = f->buffer + cnt;
263   return cnt;
264 }
265
266 static void
267 sk_fb_spout(struct fastbuf *f)
268 {
269   struct conn *c = SKIP_BACK(struct conn, tx_fb, f);
270   int len = f->bptr - f->buffer;
271   if (!len)
272     return;
273   int cnt = careful_write(c->sk, f->buffer, len);
274   if (cnt <= 0)
275     client_error("Write error");
276   f->bptr = f->buffer;
277 }
278
279 static void
280 init_sk_fastbufs(struct conn *c)
281 {
282   struct fastbuf *rf = &c->rx_fb, *tf = &c->tx_fb;
283
284   rf->buffer = xmalloc(1024);
285   rf->bufend = rf->buffer + 1024;
286   rf->bptr = rf->bstop = rf->buffer;
287   rf->name = "socket";
288   rf->refill = sk_fb_refill;
289
290   tf->buffer = xmalloc(1024);
291   tf->bufend = tf->buffer + 1024;
292   tf->bptr = tf->bstop = tf->buffer;
293   tf->name = rf->name;
294   tf->spout = sk_fb_spout;
295 }
296
297 static int
298 tls_fb_refill(struct fastbuf *f)
299 {
300   struct conn *c = SKIP_BACK(struct conn, rx_fb, f);
301   DBG("TLS: Refill");
302   int cnt = gnutls_record_recv(c->tls, f->buffer, f->bufend - f->buffer);
303   DBG("TLS: Received %d bytes", cnt);
304   if (cnt < 0)
305     client_error("TLS read error: %s", gnutls_strerror(cnt));
306   f->bptr = f->buffer;
307   f->bstop = f->buffer + cnt;
308   return cnt;
309 }
310
311 static void
312 tls_fb_spout(struct fastbuf *f)
313 {
314   struct conn *c = SKIP_BACK(struct conn, tx_fb, f);
315   int len = f->bptr - f->buffer;
316   if (!len)
317     return;
318   int cnt = gnutls_record_send(c->tls, f->buffer, len);
319   DBG("TLS: Sent %d bytes", cnt);
320   if (cnt <= 0)
321     client_error("TLS write error: %s", gnutls_strerror(cnt));
322   f->bptr = f->buffer;
323 }
324
325 static void
326 init_tls_fastbufs(struct conn *c)
327 {
328   struct fastbuf *rf = &c->rx_fb, *tf = &c->tx_fb;
329
330   ASSERT(rf->buffer && tf->buffer);     // Already set up for the plaintext connection
331   rf->refill = tls_fb_refill;
332   tf->spout = tls_fb_spout;
333 }
334
335 /*** CLIENT LOOP (runs in a child process) ***/
336
337 static void
338 sigalrm_handler(int sig UNUSED)
339 {
340   // We do not try to do any gracious shutdown to avoid races
341   client_error("Timed out");
342 }
343
344 static void
345 client_loop(struct conn *c)
346 {
347   setproctitle("submitd: client %s", c->ip_string);
348   log_pid = c->id;
349   init_sk_fastbufs(c);
350
351   signal(SIGPIPE, SIG_IGN);
352   struct sigaction sa = {
353     .sa_handler = sigalrm_handler
354   };
355   if (sigaction(SIGALRM, &sa, NULL) < 0)
356     die("Cannot setup SIGALRM handler: %m");
357
358   if (c->rule->plain_text)
359     {
360       bputsn(&c->tx_fb, "+OK");
361       bflush(&c->tx_fb);
362     }
363   else
364     {
365       bputsn(&c->tx_fb, "+TLS");
366       bflush(&c->tx_fb);
367       c->tls = tls_new_session(c->sk);
368       int err = gnutls_handshake(c->tls);
369       if (err < 0)
370         client_error("TLS handshake failed: %s", gnutls_strerror(err));
371       tls_log_params(c);
372       const char *cert_err = tls_verify_cert(c);
373       if (cert_err)
374         client_error("TLS certificate failure: %s", cert_err);
375       init_tls_fastbufs(c);
376     }
377
378   alarm(session_timeout);
379   if (!process_init(c))
380     log(L_ERROR, "Protocol handshake failed");
381   else
382     {
383       setproctitle("submitd: client %s (%s)", c->ip_string, c->user);
384       for (;;)
385         {
386           alarm(session_timeout);
387           if (!process_command(c))
388             break;
389         }
390     }
391
392   if (c->tls)
393     gnutls_bye(c->tls, GNUTLS_SHUT_WR);
394   close(c->sk);
395   if (c->tls)
396     gnutls_deinit(c->tls);
397 }
398
399 /*** MAIN LOOP ***/
400
401 static void
402 sigchld_handler(int sig UNUSED)
403 {
404   /* We do not need to do anything, just interrupt the accept syscall */
405 }
406
407 static void
408 reap_child(pid_t pid, int status)
409 {
410   byte msg[EXIT_STATUS_MSG_SIZE];
411   if (format_exit_status(msg, status))
412     log(L_ERROR, "Child %d %s", (int)pid, msg);
413
414   CLIST_FOR_EACH(struct conn *, c, connections)
415     if (c->pid == pid)
416       {
417         log(L_INFO, "Connection %d closed", c->id);
418         conn_free(c);
419         return;
420       }
421   log(L_ERROR, "Cannot find connection for child process %d", (int)pid);
422 }
423
424 static int listen_sk;
425
426 static void
427 sk_init(void)
428 {
429   listen_sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
430   if (listen_sk < 0)
431     die("socket: %m");
432   int one = 1;
433   if (setsockopt(listen_sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
434     die("setsockopt(SO_REUSEADDR): %m");
435
436   struct sockaddr_in sa;
437   bzero(&sa, sizeof(sa));
438   sa.sin_family = AF_INET;
439   sa.sin_addr.s_addr = INADDR_ANY;
440   sa.sin_port = htons(port);
441   if (bind(listen_sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
442     die("Cannot bind to port %d: %m", port);
443   if (listen(listen_sk, 1024) < 0)
444     die("Cannot listen on port %d: %m", port);
445 }
446
447 static void
448 sk_accept(void)
449 {
450   struct sockaddr_in sa;
451   int salen = sizeof(sa);
452   int sk = accept(listen_sk, (struct sockaddr *) &sa, &salen);
453   if (sk < 0)
454     {
455       if (errno == EINTR)
456         return;
457       die("accept: %m");
458     }
459
460   byte ipbuf[INET_ADDRSTRLEN];
461   inet_ntop(AF_INET, &sa.sin_addr, ipbuf, sizeof(ipbuf));
462   u32 addr = ntohl(sa.sin_addr.s_addr);
463   uns port = ntohs(sa.sin_port);
464   char *err;
465
466   struct access_rule *rule = lookup_rule(addr);
467   if (!rule)
468     {
469       err = "Unauthorized";
470       goto reject;
471     }
472
473   if (num_conn >= max_conn)
474     {
475       err = "Too many connections";
476       goto reject;
477     }
478
479   if (conn_count(addr) >= rule->max_conn)
480     {
481       err = "Too many connections from this address";
482       goto reject;
483     }
484
485   struct conn *c = conn_new();
486   log(L_INFO, "Connection from %s:%d (id %d, %s, %s)",
487         ipbuf, port, c->id,
488         (rule->plain_text ? "plain-text" : "TLS"),
489         (rule->allow_admin ? "admin" : "user"));
490   c->ip = addr;
491   c->ip_string = xstrdup(ipbuf);
492   c->sk = sk;
493   c->rule = rule;
494
495   c->pid = fork();
496   if (c->pid < 0)
497     {
498       conn_free(c);
499       err = "Server overloaded";
500       log(L_ERROR, "Fork failed: %m");
501       goto reject2;
502     }
503   if (!c->pid)
504     {
505       close(listen_sk);
506       client_loop(c);
507       exit(0);
508     }
509   close(sk);
510   return;
511
512 reject:
513   log(L_ERROR_R, "Connection from %s:%d rejected (%s)", ipbuf, port, err);
514 reject2: ;
515   // Write an error message to the socket, but do not allow it to slow us down
516   struct linger ling = { .l_onoff=0 };
517   if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0)
518     log(L_ERROR, "Cannot set SO_LINGER: %m");
519   write(sk, "-", 1);
520   write(sk, err, strlen(err));
521   write(sk, "\n", 1);
522   close(sk);
523 }
524
525 int main(int argc, char **argv)
526 {
527   setproctitle_init(argc, argv);
528   cf_def_file = "submit/config";
529   cf_declare_section("SubmitD", &submitd_conf, 0);
530   cf_declare_section("Tasks", &tasks_conf, 0);
531
532   int opt;
533   if ((opt = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
534     die("This program has no options");
535
536   log_file(log_name);
537
538   log(L_INFO, "Initializing TLS");
539   tls_init();
540
541   conn_init();
542   sk_init();
543   log(L_INFO, "Listening on port %d", port);
544
545   struct sigaction sa = {
546     .sa_handler = sigchld_handler
547   };
548   if (sigaction(SIGCHLD, &sa, NULL) < 0)
549     die("Cannot setup SIGCHLD handler: %m");
550
551   for (;;)
552     {
553       setproctitle("submitd: %d connections", num_conn);
554       int status;
555       pid_t pid = waitpid(-1, &status, WNOHANG);
556       if (pid > 0)
557         reap_child(pid, status);
558       else
559         sk_accept();
560     }
561 }