chiark / gitweb /
Use users.db. trackdb* moves to lib/, as it's now used by client.c to
[disorder] / server / server.c
index 5d88db4f04aac15a2323c812c0eb763d3ba11388..145bfe222f9c277de23ee84e51a3e80833cfc1be 100644 (file)
@@ -64,6 +64,7 @@
 #include "defs.h"
 #include "cache.h"
 #include "unicode.h"
+#include "cookies.h"
 
 #ifndef NONCE_SIZE
 # define NONCE_SIZE 16
@@ -107,6 +108,8 @@ struct conn {
   struct eventlog_output *lo;
   /** @brief Parent listener */
   const struct listener *l;
+  /** @brief Login cookie or NULL */
+  char *cookie;
 };
 
 static int reader_callback(ev_source *ev,
@@ -225,7 +228,7 @@ static int c_play(struct conn *c, char **vec,
    * anything. */
   if(q == qhead.next && playing)
     prepare(c->ev, q);
-  sink_writes(ev_writer_sink(c->w), "250 queued\n");
+  sink_printf(ev_writer_sink(c->w), "252 %s\n", q->id);
   /* If the queue was empty but we are for some reason paused then
    * unpause. */
   if(!playing) resume_playing(0);
@@ -346,7 +349,7 @@ static int c_version(struct conn *c,
                     char attribute((unused)) **vec,
                     int attribute((unused)) nvec) {
   /* VERSION had better only use the basic character set */
-  sink_printf(ev_writer_sink(c->w), "251 %s\n", disorder_version_string);
+  sink_printf(ev_writer_sink(c->w), "251 %s\n", disorder_short_version_string);
   return 1;                    /* completed */
 }
 
@@ -370,55 +373,62 @@ static int c_become(struct conn *c,
   return 1;
 }
 
-static int c_user(struct conn *c,
-                 char **vec,
-                 int attribute((unused)) nvec) {
-  int n;
-  const char *res;
+static const char *connection_host(struct conn *c) {
   union {
     struct sockaddr sa;
     struct sockaddr_in in;
     struct sockaddr_in6 in6;
   } u;
   socklen_t l;
+  int n;
   char host[1024];
 
-  if(c->who) {
-    sink_writes(ev_writer_sink(c->w), "530 already authenticated\n");
-    return 1;
-  }
   /* get connection data */
   l = sizeof u;
   if(getpeername(c->fd, &u.sa, &l) < 0) {
     error(errno, "S%x error calling getpeername", c->tag);
-    sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
-    return 1;
+    return 0;
   }
   if(c->l->pf != PF_UNIX) {
     if((n = getnameinfo(&u.sa, l,
                        host, sizeof host, 0, 0, NI_NUMERICHOST))) {
       error(0, "S%x error calling getnameinfo: %s", c->tag, gai_strerror(n));
-      sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
-      return 1;
+      return 0;
     }
+    return xstrdup(host);
   } else
-    strcpy(host, "local");
+    return "local";
+}
+
+static int c_user(struct conn *c,
+                 char **vec,
+                 int attribute((unused)) nvec) {
+  const char *res, *host, *password;
+
+  if(c->who) {
+    sink_writes(ev_writer_sink(c->w), "530 already authenticated\n");
+    return 1;
+  }
+  /* get connection data */
+  if(!(host = connection_host(c))) {
+    sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
+    return 1;
+  }
   /* find the user */
-  for(n = 0; n < config->allow.n
-       && strcmp(config->allow.s[n].s[0], vec[0]); ++n)
-    ;
-  /* if it's a real user check whether the response is right */
-  if(n >= config->allow.n) {
+  password = trackdb_get_password(vec[0]);
+  /* reject nonexistent users */
+  if(!password) {
     info("S%x unknown user '%s' from %s", c->tag, vec[0], host);
     sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
     return 1;
   }
-  res = authhash(c->nonce, sizeof c->nonce, config->allow.s[n].s[1],
+  /* check whether the response is right */
+  res = authhash(c->nonce, sizeof c->nonce, password,
                 config->authorization_algorithm);
   if(wideopen || (res && !strcmp(res, vec[1]))) {
     c->who = vec[0];
     /* currently we only bother logging remote connections */
-    if(c->l->pf != PF_UNIX)
+    if(strcmp(host, "local"))
       info("S%x %s connected from %s", c->tag, vec[0], host);
     sink_writes(ev_writer_sink(c->w), "230 OK\n");
     return 1;
@@ -962,7 +972,97 @@ static int c_rtp_address(struct conn *c,
     sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
   return 1;
 }
+
+static int c_cookie(struct conn *c,
+                   char **vec,
+                   int attribute((unused)) nvec) {
+  const char *host;
+  char *user;
+
+  /* Can't log in twice on the same connection */
+  if(c->who) {
+    sink_writes(ev_writer_sink(c->w), "530 already authenticated\n");
+    return 1;
+  }
+  /* Get some kind of peer identifcation */
+  if(!(host = connection_host(c))) {
+    sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
+    return 1;
+  }
+  /* Check the cookie */
+  user = verify_cookie(vec[0]);
+  if(!user) {
+    sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
+    return 1;
+  }
+  /* Log in */
+  c->who = user;
+  c->cookie = vec[0];
+  if(strcmp(host, "local"))
+    info("S%x %s connected with cookie from %s", c->tag, user, host);
+  sink_writes(ev_writer_sink(c->w), "230 OK\n");
+  return 1;
+}
+
+static int c_make_cookie(struct conn *c,
+                        char attribute((unused)) **vec,
+                        int attribute((unused)) nvec) {
+  const char *cookie = make_cookie(c->who);
+
+  if(cookie)
+    sink_printf(ev_writer_sink(c->w), "252 %s\n", cookie);
+  else
+    sink_writes(ev_writer_sink(c->w), "550 Cannot create cookie\n");
+  return 1;
+}
+
+static int c_revoke(struct conn *c,
+                   char attribute((unused)) **vec,
+                   int attribute((unused)) nvec) {
+  if(c->cookie) {
+    revoke_cookie(c->cookie);
+    sink_writes(ev_writer_sink(c->w), "250 OK\n");
+  } else
+    sink_writes(ev_writer_sink(c->w), "550 Did not log in with cookie\n");
+  return 1;
+}
+
+static int c_adduser(struct conn *c,
+                    char **vec,
+                    int attribute((unused)) nvec) {
+  /* TODO local only */
+  if(trackdb_adduser(vec[0], vec[1], default_rights(), 0))
+    sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n");
+  else
+    sink_writes(ev_writer_sink(c->w), "250 User created\n");
+  return 1;
+}
+
+static int c_deluser(struct conn *c,
+                    char **vec,
+                    int attribute((unused)) nvec) {
+  /* TODO local only */
+  if(trackdb_deluser(vec[0]))
+    sink_writes(ev_writer_sink(c->w), "550 Cannot deleted user\n");
+  else
+    sink_writes(ev_writer_sink(c->w), "250 User deleted\n");
+  return 1;
+}
+
+static int c_edituser(struct conn *c,
+                     char attribute((unused)) **vec,
+                     int attribute((unused)) nvec) {
+  sink_writes(ev_writer_sink(c->w), "550 Not implemented\n"); /* TODO */
+  return 1;
+}
+
+static int c_userinfo(struct conn *c,
+                     char attribute((unused)) **vec,
+                     int attribute((unused)) nvec) {
+  sink_writes(ev_writer_sink(c->w), "550 Not implemented\n"); /* TODO */
+  return 1;
+}
+
 #define C_AUTH         0001            /* must be authenticated */
 #define C_TRUSTED      0002            /* must be trusted user */
 
@@ -972,10 +1072,14 @@ static const struct command {
   int (*fn)(struct conn *, char **, int);
   unsigned flags;
 } commands[] = {
+  { "adduser",        2, 2,       c_adduser,        C_AUTH|C_TRUSTED },
   { "allfiles",       0, 2,       c_allfiles,       C_AUTH },
   { "become",         1, 1,       c_become,         C_AUTH|C_TRUSTED },
+  { "cookie",         1, 1,       c_cookie,         0 },
+  { "deluser",        1, 1,       c_deluser,        C_AUTH|C_TRUSTED },
   { "dirs",           0, 2,       c_dirs,           C_AUTH },
   { "disable",        0, 1,       c_disable,        C_AUTH },
+  { "edituser",       3, 3,       c_edituser,       C_AUTH },
   { "enable",         0, 0,       c_enable,         C_AUTH },
   { "enabled",        0, 0,       c_enabled,        C_AUTH },
   { "exists",         1, 1,       c_exists,         C_AUTH },
@@ -984,6 +1088,7 @@ static const struct command {
   { "get-global",     1, 1,       c_get_global,     C_AUTH },
   { "length",         1, 1,       c_length,         C_AUTH },
   { "log",            0, 0,       c_log,            C_AUTH },
+  { "make-cookie",    0, 0,       c_make_cookie,    C_AUTH },
   { "move",           2, 2,       c_move,           C_AUTH },
   { "moveafter",      1, INT_MAX, c_moveafter,      C_AUTH },
   { "new",            0, 1,       c_new,            C_AUTH },
@@ -1003,6 +1108,7 @@ static const struct command {
   { "rescan",         0, 0,       c_rescan,         C_AUTH|C_TRUSTED },
   { "resolve",        1, 1,       c_resolve,        C_AUTH },
   { "resume",         0, 0,       c_resume,         C_AUTH },
+  { "revoke",         0, 0,       c_revoke,         C_AUTH },
   { "rtp-address",    0, 0,       c_rtp_address,    C_AUTH },
   { "scratch",        0, 1,       c_scratch,        C_AUTH },
   { "search",         1, 1,       c_search,         C_AUTH },
@@ -1012,8 +1118,9 @@ static const struct command {
   { "stats",          0, 0,       c_stats,          C_AUTH },
   { "tags",           0, 0,       c_tags,           C_AUTH },
   { "unset",          2, 2,       c_set,            C_AUTH },
-  { "unset-global",   1, 1,       c_set_global,      C_AUTH },
+  { "unset-global",   1, 1,       c_set_global,     C_AUTH },
   { "user",           2, 2,       c_user,           0 },
+  { "userinfo",       2, 2,       c_userinfo,       C_AUTH },
   { "version",        0, 0,       c_version,        C_AUTH },
   { "volume",         0, 2,       c_volume,         C_AUTH }
 };