all: submitd connect
-submitd: submitd.o lib/libucw.a lib/libsh.a
-submitd.o: submitd.c
+submitd: submitd.o commands.o lib/libsh.a lib/libucw.a
+submitd.o: submitd.c submitd.h
+commands.o: commands.c submitd.h
connect: connect.o lib/libucw.a
connect.o: connect.c
--- /dev/null
+/*
+ * The Submit Daemon: Processing of Commands
+ *
+ * (c) 2007 Martin Mares <mj@ucw.cz>
+ */
+
+#include "lib/lib.h"
+#include "lib/mempool.h"
+#include "lib/stkstring.h"
+#include "sherlock/object.h"
+#include "sherlock/objread.h"
+
+#include <sys/stat.h>
+
+#include "submitd.h"
+
+static void NONRET
+read_error_cb(struct obj_read_state *st UNUSED, byte *msg)
+{
+ client_error("Request parse error: %s", msg);
+}
+
+static int
+read_request(struct conn *c)
+{
+ if (c->pool)
+ mp_flush(c->pool);
+ else
+ c->pool = mp_new(1024);
+ c->request = obj_new(c->pool);
+ c->reply = obj_new(c->pool);
+
+ struct obj_read_state st;
+ obj_read_start(&st, c->request);
+ st.error_callback = read_error_cb;
+ byte line[1024];
+ uns size = 0;
+ for (;;)
+ {
+ int l = bgets_nodie(&c->rx_fb, line, sizeof(line));
+ if (l < 0)
+ client_error("Request line too long");
+ if (!l)
+ {
+ if (!size)
+ return 0;
+ else
+ client_error("Truncated request");
+ }
+ if (l == 1)
+ break;
+ size += l;
+ if (size >= max_request_size)
+ client_error("Request too long");
+ obj_read_attr(&st, line[0], line+1);
+ }
+ obj_read_end(&st);
+ return 1;
+}
+
+static void
+write_reply(struct conn *c)
+{
+ if (trace_commands)
+ {
+ byte *msg;
+ if (msg = obj_find_aval(c->reply, '-'))
+ log(L_DEBUG, ">> -%s", msg);
+ else if (msg = obj_find_aval(c->reply, '+'))
+ log(L_DEBUG, ">> +%s", msg);
+ else
+ log(L_DEBUG, ">> ???");
+ }
+ put_attr_set_type(BUCKET_TYPE_PLAIN);
+ bput_object(&c->tx_fb, c->reply);
+ bputc(&c->tx_fb, '\n');
+ bflush(&c->tx_fb);
+}
+
+static void
+execute_command(struct conn *c)
+{
+ byte *cmd = obj_find_aval(c->request, '!');
+ if (!cmd)
+ {
+ obj_set_attr(c->reply, '-', "Missing command");
+ return;
+ }
+ if (trace_commands)
+ log(L_DEBUG, "<< %s", cmd);
+ obj_set_attr(c->reply, '-', "Unknown command");
+}
+
+int
+process_command(struct conn *c)
+{
+ if (!read_request(c))
+ return 0;
+ execute_command(c);
+ write_reply(c);
+ return 1;
+}
+
+static int
+user_exists_p(byte *user)
+{
+ byte *fn = stk_printf("solutions/%s/status", user);
+ struct stat st;
+ return !stat(fn, &st) && S_ISREG(st.st_mode);
+}
+
+static void
+execute_init(struct conn *c)
+{
+ byte *user = obj_find_aval(c->request, 'U');
+ if (!user)
+ {
+ obj_set_attr(c->reply, '-', "Missing user");
+ return;
+ }
+ if (!c->cert_name ||
+ !strcmp(user, c->cert_name) ||
+ c->rule->allow_admin && !strcmp(c->cert_name, "admin"))
+ {
+ if (!user_exists_p(user))
+ {
+ obj_set_attr(c->reply, '-', "Unknown user");
+ return;
+ }
+ log(L_INFO, "Logged in %s", user);
+ }
+ else
+ {
+ obj_set_attr(c->reply, '-', "Permission denied");
+ log(L_ERROR, "Unauthorized attempt to log in as %s", user);
+ return;
+ }
+ obj_set_attr(c->reply, '+', "OK");
+}
+
+int
+process_init(struct conn *c)
+{
+ if (!read_request(c))
+ return 0;
+ execute_init(c);
+ write_reply(c);
+ return !!obj_find_attr(c->reply, '+');
+}
for (;;)
{
byte buf[1024];
- if (!fgets(buf, sizeof(buf), stdin))
- break;
- int len = strlen(buf);
- err = gnutls_record_send(s, buf, len); TLS_CHECK(gnutls_record_send);
- err = gnutls_record_recv(s, buf, len); TLS_CHECK(gnutls_record_recv);
- if (!err)
+ do
{
- log(L_INFO, "Connection closed");
- break;
+ if (!fgets(buf, sizeof(buf), stdin))
+ goto done;
+ int len = strlen(buf);
+ err = gnutls_record_send(s, buf, len); TLS_CHECK(gnutls_record_send);
}
- fwrite(buf, 1, err, stdout);
+ while (buf[0] != '\n');
+ int last = 0;
+ for (;;)
+ {
+ err = gnutls_record_recv(s, buf, sizeof(buf)); TLS_CHECK(gnutls_record_recv);
+ if (!err)
+ {
+ log(L_INFO, "Connection closed");
+ break;
+ }
+ fwrite(buf, 1, err, stdout);
+ for (int i=0; i<err; i++)
+ {
+ if (buf[i] == '\n' && last == '\n')
+ goto next;
+ last = buf[i];
+ }
+ }
+next:
fflush(stdout);
}
+done:
gnutls_bye(s, GNUTLS_SHUT_RDWR);
close(sk);
gnutls_deinit(s);
#include "lib/lib.h"
#include "lib/conf.h"
#include "lib/getopt.h"
-#include "lib/ipaccess.h"
-#include "lib/fastbuf.h"
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netinet/in.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
+
+#include "submitd.h"
/*** CONFIGURATION ***/
static byte *server_cert_name = "?";
static byte *server_key_name = "?";
static clist access_rules;
-
-struct access_rule {
- cnode n;
- struct ip_addrmask addrmask;
- uns allow_admin;
- uns plain_text;
- uns max_conn;
-};
+static uns trace_tls;
+uns max_request_size;
+uns max_attachment_size;
+uns trace_commands;
static struct cf_section access_conf = {
CF_TYPE(struct access_rule),
CF_UNS("DHBits", &dh_bits),
CF_UNS("MaxConn", &max_conn),
CF_UNS("SessionTimeout", &session_timeout),
+ CF_UNS("MaxRequestSize", &max_request_size),
+ CF_UNS("MaxAttachSize", &max_attachment_size),
CF_STRING("CACert", &ca_cert_name),
CF_STRING("ServerCert", &server_cert_name),
CF_STRING("ServerKey", &server_key_name),
CF_LIST("Access", &access_rules, &access_conf),
+ CF_UNS("TraceTLS", &trace_tls),
+ CF_UNS("TraceCommands", &trace_commands),
CF_END
}
};
/*** CONNECTIONS ***/
-struct conn {
- cnode n;
- u32 ip; // Used by the main loop connection logic
- pid_t pid;
- uns id;
- struct access_rule *rule; // Rule matched by this connection
- int sk; // Client socket
- gnutls_session_t tls; // TLS session
- struct fastbuf rx_fb, tx_fb; // Fastbufs for communication with the client
-};
-
static clist connections;
static uns last_conn_id;
static uns num_conn;
static struct conn *
conn_new(void)
{
- struct conn *c = xmalloc(sizeof(*c));
+ struct conn *c = xmalloc_zero(sizeof(*c));
c->id = ++last_conn_id;
clist_add_tail(&connections, &c->n);
num_conn++;
}
static const char *
-tls_verify_cert(gnutls_session_t s)
+tls_verify_cert(struct conn *c)
{
+ gnutls_session_t s = c->tls;
uns status, num_certs;
int err;
gnutls_x509_crt_t cert;
err = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &dn_len);
if (err < 0)
return "Cannot retrieve common name";
- log(L_INFO, "Cert CN: %s", dn);
+ if (trace_tls)
+ log(L_INFO, "Cert CN: %s", dn);
+ c->cert_name = xstrdup(dn);
/* Check certificate purpose */
byte purp[256];
}
static void
-tls_log_params(gnutls_session_t s)
+tls_log_params(struct conn *c)
{
+ if (!trace_tls)
+ return;
+ gnutls_session_t s = c->tls;
const char *proto = gnutls_protocol_get_name(gnutls_protocol_get_version(s));
const char *kx = gnutls_kx_get_name(gnutls_kx_get(s));
const char *cert = gnutls_certificate_type_get_name(gnutls_certificate_type_get(s));
/*** SOCKET FASTBUFS ***/
-static void NONRET
+void NONRET
client_error(char *msg, ...)
{
va_list args;
int err = gnutls_handshake(c->tls);
if (err < 0)
client_error("TLS handshake failed: %s", gnutls_strerror(err));
- tls_log_params(c->tls);
- const char *cert_err = tls_verify_cert(c->tls);
+ tls_log_params(c);
+ const char *cert_err = tls_verify_cert(c);
if (cert_err)
client_error("TLS certificate failure: %s", cert_err);
init_tls_fastbufs(c);
}
- for (;;)
- {
- alarm(session_timeout);
- byte buf[1024];
- if (!bgets(&c->rx_fb, buf, sizeof(buf)))
- break;
- bputsn(&c->tx_fb, buf);
- bflush(&c->tx_fb);
- }
+ alarm(session_timeout);
+ if (!process_init(c))
+ log(L_ERROR, "Protocol handshake failed");
+ else
+ for (;;)
+ {
+ alarm(session_timeout);
+ if (!process_command(c))
+ break;
+ }
if (c->tls)
gnutls_bye(c->tls, GNUTLS_SHUT_WR);
--- /dev/null
+/*
+ * The Submit Daemon
+ *
+ * (c) 2007 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _SUBMITD_H
+#define _SUBMITD_H
+
+#include "lib/clists.h"
+#include "lib/ipaccess.h"
+#include "lib/fastbuf.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+struct access_rule {
+ cnode n;
+ struct ip_addrmask addrmask;
+ uns allow_admin;
+ uns plain_text;
+ uns max_conn;
+};
+
+struct conn {
+ // Set up by the master process
+ cnode n;
+ u32 ip;
+ pid_t pid;
+ uns id;
+ struct access_rule *rule; // Rule matched by this connection
+ int sk; // Client socket
+ byte *cert_name; // Client name from the certificate (NULL if no TLS)
+
+ // Used by the child process
+ gnutls_session_t tls; // TLS session
+ struct fastbuf rx_fb, tx_fb; // Fastbufs for communication with the client (either plain-text or TLS)
+ struct mempool *pool;
+ struct odes *request;
+ struct odes *reply;
+ byte *user;
+};
+
+extern uns max_request_size, max_attachment_size, trace_commands;
+
+/* submitd.c */
+
+void NONRET client_error(char *msg, ...);
+
+/* commands.c */
+
+int process_init(struct conn *c);
+int process_command(struct conn *c);
+
+#endif