chiark / gitweb /
support alternative hashes for authentication
authorRichard Kettlewell <rjk@greenend.org.uk>
Tue, 2 Oct 2007 10:23:41 +0000 (11:23 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Tue, 2 Oct 2007 10:23:41 +0000 (11:23 +0100)
doc/disorder_config.5.in
doc/disorder_protocol.5.in
lib/authhash.c
lib/authhash.h
lib/client.c
lib/configuration.c
lib/configuration.h
lib/eclient.c
server/Makefile.am
server/server.c

index 4cde04fcf7bd388c7453a47c6cdca0580e6f50c7..b1ea0b1fa6de6a84e5b91edc5d9be10bc16ed0c6 100644 (file)
@@ -142,6 +142,12 @@ automatically included, but should include the proper extension.
 .IP
 The default is \fB{/artist}{/album}{/title}{ext}\fR.
 .TP
 .IP
 The default is \fB{/artist}{/album}{/title}{ext}\fR.
 .TP
+.B authorization_algorthm \fIALGORITHM\fR
+Defines the algorithm used to authenticate clients.  The valid options
+are sha1 (the default), sha256, sha384 and sha512.  See
+.BR disorder_protocol (5)
+for more details.
+.TP
 .B broadcast \fIADDRESS\fR \fIPORT\fR
 Transmit sound data to \fIADDRESS\fR using UDP port \fIPORT\fR.  This implies
 \fBspeaker_backend network\fR.
 .B broadcast \fIADDRESS\fR \fIPORT\fR
 Transmit sound data to \fIADDRESS\fR using UDP port \fIPORT\fR.  This implies
 \fBspeaker_backend network\fR.
index 07fada9bd9694505170f347e7ced345dd9cc5008..9e64d947b6c811f4c2c3f7f791074db2e0aaff6d 100644 (file)
@@ -231,10 +231,10 @@ Unset a global preference.
 Authenticate as \fIUSER\fR.
 .IP
 When a connection is made the server sends a \fB221\fR response before any
 Authenticate as \fIUSER\fR.
 .IP
 When a connection is made the server sends a \fB221\fR response before any
-command is received.  As its first field this contains a challenge string
-encoded in hex.
+command is received.  This contains an algorithm name and a challenge encoded
+in hex.  Currently the algorithm name is omitted if it is "sha1".
 .IP
 .IP
-The \fIRESPONSE\fR consists of the SHA-1 hash of the user's password
+The \fIRESPONSE\fR consists of the selected hash of the user's password
 concatenated with the challenge, encoded in hex.
 .TP
 .B version
 concatenated with the challenge, encoded in hex.
 .TP
 .B version
index b97204d9364a76aee5479419c4c8374d17045ce5..1eaf27f2f504f840d69618d06eab50ad6a414557 100644 (file)
 #include "log.h"
 #include "authhash.h"
 
 #include "log.h"
 #include "authhash.h"
 
-#ifndef AUTHHASH
-/** @brief Which hash function to use */
-# define AUTHHASH GCRY_MD_SHA1
-#endif
+/** @brief Structure of algorithm lookup table */
+struct algorithm {
+  const char *name;
+  int id;
+};
+
+/** @brief Algorithm lookup table
+ *
+ * We don't use gcry_md_map_name() since that would import gcrypt's API into
+ * the disorder protocol.
+ */
+static const struct algorithm algorithms[] = {
+  { "SHA1", GCRY_MD_SHA1 },
+  { "sha1", GCRY_MD_SHA1 },
+  { "SHA256", GCRY_MD_SHA256 },
+  { "sha256", GCRY_MD_SHA256 },
+  { "SHA384", GCRY_MD_SHA384 },
+  { "sha384", GCRY_MD_SHA384 },
+  { "SHA512", GCRY_MD_SHA512 },
+  { "sha512", GCRY_MD_SHA512 },
+};
+
+/** @brief Number of supported algorithms */
+#define NALGORITHMS (sizeof algorithms / sizeof *algorithms)
 
 /** @brief Perform the authorization hash function
  * @param challenge Pointer to challange
  * @param nchallenge Size of challenge
  * @param password Password
 
 /** @brief Perform the authorization hash function
  * @param challenge Pointer to challange
  * @param nchallenge Size of challenge
  * @param password Password
+ * @param algo Algorithm to use
  *
  * Computes H(challenge|password) and returns it as a newly allocated hex
  *
  * Computes H(challenge|password) and returns it as a newly allocated hex
- * string.  Currently the hash function is SHA-1, but this may be changed in
- * future versions; see @ref AUTHHASH.
+ * string, or returns NULL on error.
  */
 const char *authhash(const void *challenge, size_t nchallenge,
  */
 const char *authhash(const void *challenge, size_t nchallenge,
-                    const char *password) {
+                    const char *password, const char *algo) {
   gcrypt_hash_handle h;
   const char *res;
   gcrypt_hash_handle h;
   const char *res;
+  size_t n;
+  int id;
 
   assert(challenge != 0);
   assert(password != 0);
 
   assert(challenge != 0);
   assert(password != 0);
+  assert(algo != 0);
+  for(n = 0; n < NALGORITHMS; ++n)
+    if(!strcmp(algo, algorithms[n].name))
+      break;
+  if(n >= NALGORITHMS)
+    return NULL;
+  id = algorithms[n].id;
 #if HAVE_GCRY_ERROR_T
   {
     gcry_error_t e;
     
 #if HAVE_GCRY_ERROR_T
   {
     gcry_error_t e;
     
-    if((e = gcry_md_open(&h, AUTHHASH, 0))) {
+    if((e = gcry_md_open(&h, id, 0))) {
       error(0, "gcry_md_open: %s", gcry_strerror(e));
       return 0;
     }
   }
 #else
       error(0, "gcry_md_open: %s", gcry_strerror(e));
       return 0;
     }
   }
 #else
-  h = gcry_md_open(AUTHHASH, 0);
+  h = gcry_md_open(id, 0);
 #endif
   gcry_md_write(h, password, strlen(password));
   gcry_md_write(h, challenge, nchallenge);
 #endif
   gcry_md_write(h, password, strlen(password));
   gcry_md_write(h, challenge, nchallenge);
-  res = hex(gcry_md_read(h, AUTHHASH), gcry_md_get_algo_dlen(AUTHHASH));
+  res = hex(gcry_md_read(h, id), gcry_md_get_algo_dlen(id));
   gcry_md_close(h);
   return res;
 }
 
   gcry_md_close(h);
   return res;
 }
 
+/** @brief Return non-zero if @p algo is a valid algorithm */
+int valid_authhash(const char *algo) {
+  size_t n;
+
+  for(n = 0; n < NALGORITHMS; ++n)
+    if(!strcmp(algo, algorithms[n].name))
+      return 1;
+  return 0;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
 /*
 Local Variables:
 c-basic-offset:2
index 99992793b6edeb612aeb18bc3d31c7e64dc96eb9..38981e10b4f700c8f53f0fea10aa122c80473f0b 100644 (file)
@@ -21,7 +21,8 @@
 #define AUTHHASH_H
 
 const char *authhash(const void *challenge, size_t nchallenge,
 #define AUTHHASH_H
 
 const char *authhash(const void *challenge, size_t nchallenge,
-                    const char *user);
+                    const char *user, const char *algo);
+int valid_authhash(const char *algo);
 
 #endif /* AUTHHASH_H */
 
 
 #endif /* AUTHHASH_H */
 
index 97dde93825ddef6c019e32f5b6bfb6498cba747a..979e54ab9432b20c5c28d781ec05cb95babb74b0 100644 (file)
@@ -207,11 +207,12 @@ int disorder_connect_sock(disorder_client *c,
                          const char *username,
                          const char *password,
                          const char *ident) {
                          const char *username,
                          const char *password,
                          const char *ident) {
-  int fd = -1, fd2 = -1;
+  int fd = -1, fd2 = -1, nrvec;
   unsigned char *nonce;
   size_t nl;
   const char *res;
   unsigned char *nonce;
   size_t nl;
   const char *res;
-  char *r;
+  char *r, **rvec;
+  const char *algo = "SHA1";
 
   if(!password) {
     error(0, "no password found");
 
   if(!password) {
     error(0, "no password found");
@@ -243,9 +244,17 @@ int disorder_connect_sock(disorder_client *c,
   c->ident = xstrdup(ident);
   if(disorder_simple(c, &r, 0, (const char *)0))
     return -1;
   c->ident = xstrdup(ident);
   if(disorder_simple(c, &r, 0, (const char *)0))
     return -1;
-  if(!(nonce = unhex(r, &nl)))
+  if(!(rvec = split(r, &nrvec, SPLIT_QUOTES, 0, 0)))
+    return -1;
+  if(nrvec > 1) {
+    algo = *rvec++;
+    --nrvec;
+  }
+  if(!nrvec)
+    return -1;
+  if(!(nonce = unhex(*rvec, &nl)))
     return -1;
     return -1;
-  if(!(res = authhash(nonce, nl, password))) goto error;
+  if(!(res = authhash(nonce, nl, password, algo))) goto error;
   if(disorder_simple(c, 0, "user", username, res, (char *)0))
     return -1;
   c->user = xstrdup(username);
   if(disorder_simple(c, 0, "user", username, res, (char *)0))
     return -1;
   c->user = xstrdup(username);
index ae71d6321c32943e28e680f25f79effd839bf319..ad410245b5e752031b803f1f73a5e46836b5d9c5 100644 (file)
@@ -51,6 +51,7 @@
 #include "printf.h"
 #include "regsub.h"
 #include "signame.h"
 #include "printf.h"
 #include "regsub.h"
 #include "signame.h"
+#include "authhash.h"
 
 /** @brief Path to config file 
  *
 
 /** @brief Path to config file 
  *
@@ -812,6 +813,20 @@ static int validate_port(const struct config_state attribute((unused)) *cs,
   }
 }
 
   }
 }
 
+static int validate_algo(const struct config_state attribute((unused)) *cs,
+                        int nvec,
+                        char **vec) {
+  if(nvec != 1) {
+    error(0, "%s:%d: invalid algorithm specification", cs->path, cs->line);
+    return -1;
+  }
+  if(!valid_authhash(vec[0])) {
+    error(0, "%s:%d: unsuported algorithm '%s'", cs->path, cs->line, vec[0]);
+    return -1;
+  }
+  return 0;
+}
+
 /** @brief Item name and and offset */
 #define C(x) #x, offsetof(struct config, x)
 /** @brief Item name and and offset */
 /** @brief Item name and and offset */
 #define C(x) #x, offsetof(struct config, x)
 /** @brief Item name and and offset */
@@ -821,6 +836,7 @@ static int validate_port(const struct config_state attribute((unused)) *cs,
 static const struct conf conf[] = {
   { C(alias),            &type_string,           validate_alias },
   { C(allow),            &type_stringlist_accum, validate_allow },
 static const struct conf conf[] = {
   { C(alias),            &type_string,           validate_alias },
   { C(allow),            &type_stringlist_accum, validate_allow },
+  { C(authorization_algorithm), &type_string,    validate_algo },
   { C(broadcast),        &type_stringlist,       validate_addrport },
   { C(broadcast_from),   &type_stringlist,       validate_addrport },
   { C(channel),          &type_string,           validate_channel },
   { C(broadcast),        &type_stringlist,       validate_addrport },
   { C(broadcast_from),   &type_stringlist,       validate_addrport },
   { C(channel),          &type_string,           validate_channel },
@@ -975,6 +991,7 @@ static struct config *config_default(void) {
   c->queue_pad = 10;
   c->speaker_backend = -1;
   c->multicast_ttl = 1;
   c->queue_pad = 10;
   c->speaker_backend = -1;
   c->multicast_ttl = 1;
+  c->authorization_algorithm = xstrdup("sha1");
   return c;
 }
 
   return c;
 }
 
index 84b1eb858de8601fd82620fb84cf35d190c9124a..e9893f0f0dd16bd6c6c22013ecd57ee86b6c49b6 100644 (file)
@@ -96,6 +96,9 @@ struct transformlist {
 struct config {
   /* server config */
 
 struct config {
   /* server config */
 
+  /** @brief Authorization algorithm */
+  char *authorization_algorithm;
+  
   /** @brief All players */
   struct stringlistlist player;
 
   /** @brief All players */
   struct stringlistlist player;
 
index e5b723de7f8b824cbf2201f82a45a93d07d1969f..766167958a16a845469ad7539d7fb201f7cc1cba 100644 (file)
@@ -503,16 +503,31 @@ static void authbanner_opcallback(disorder_eclient *c,
   size_t nonce_len;
   const unsigned char *nonce;
   const char *res;
   size_t nonce_len;
   const unsigned char *nonce;
   const char *res;
+  char **rvec;
+  int nrvec;
+  const char *algo = "SHA1";
   
   D(("authbanner_opcallback"));
   
   D(("authbanner_opcallback"));
-  if(c->rc / 100 != 2) {
-    /* Banner told us to go away.  We cannot proceed. */
+  if(c->rc / 100 != 2
+     || !(rvec = split(c->line + 4, &nrvec, SPLIT_QUOTES, 0, 0))
+     || nrvec < 1) {
+    /* Banner told us to go away, or was malformed.  We cannot proceed. */
     protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
     disorder_eclient_close(c);
     return;
   }
     protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
     disorder_eclient_close(c);
     return;
   }
+  if(nrvec > 1) {
+    algo = *rvec++;
+    --nrvec;
+  }
   nonce = unhex(c->line +  4, &nonce_len);
   nonce = unhex(c->line +  4, &nonce_len);
-  res = authhash(nonce, nonce_len, config->password);
+  res = authhash(nonce, nonce_len, config->password, algo);
+  if(!res) {
+    protocol_error(c, op, c->rc, "%s: unknown authentication algorithm '%s'",
+                   c->ident, algo);
+    disorder_eclient_close(c);
+    return;
+  }
   stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/,
                 "user", quoteutf8(config->username), quoteutf8(res),
                 (char *)0);
   stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/,
                 "user", quoteutf8(config->username), quoteutf8(res),
                 (char *)0);
index e63f6aa1966efbda29aa3debbff721cda6a8b8af..2b655956128aada719294b48f18fd1a5cb932c42 100644 (file)
@@ -41,7 +41,7 @@ disorderd_DEPENDENCIES=../lib/libdisorder.a
 disorder_deadlock_SOURCES=deadlock.c                    \
        trackdb.c trackdb.h
 disorder_deadlock_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
 disorder_deadlock_SOURCES=deadlock.c                    \
        trackdb.c trackdb.h
 disorder_deadlock_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
-       $(LIBDB) $(LIBPCRE) $(LIBICONV)
+       $(LIBDB) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
 disorder_deadlock_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_speaker_SOURCES=speaker.c speaker.h \
 disorder_deadlock_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_speaker_SOURCES=speaker.c speaker.h \
@@ -58,7 +58,8 @@ disorder_decode_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
 disorder_decode_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_normalize_SOURCES=normalize.c
 disorder_decode_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_normalize_SOURCES=normalize.c
-disorder_normalize_LDADD=$(LIBOBJS) ../lib/libdisorder.a $(LIBPCRE) $(LIBICONV)
+disorder_normalize_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
+       $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
 disorder_normalize_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_rescan_SOURCES=rescan.c                        \
 disorder_normalize_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_rescan_SOURCES=rescan.c                        \
@@ -66,7 +67,7 @@ disorder_rescan_SOURCES=rescan.c                        \
        trackdb.c trackdb.h exports.c                   \
        ../lib/memgc.c
 disorder_rescan_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
        trackdb.c trackdb.h exports.c                   \
        ../lib/memgc.c
 disorder_rescan_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
-       $(LIBDB) $(LIBGC) $(LIBPCRE) $(LIBICONV)
+       $(LIBDB) $(LIBGC) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
 disorder_rescan_LDFLAGS=-export-dynamic
 disorder_rescan_DEPENDENCIES=../lib/libdisorder.a
 
 disorder_rescan_LDFLAGS=-export-dynamic
 disorder_rescan_DEPENDENCIES=../lib/libdisorder.a
 
@@ -74,7 +75,7 @@ disorder_dump_SOURCES=dump.c                                  \
         trackdb.c trackdb.h                            \
        ../lib/memgc.c
 disorder_dump_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
         trackdb.c trackdb.h                            \
        ../lib/memgc.c
 disorder_dump_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
-       $(LIBPCRE) $(LIBDB) $(LIBICONV) $(LIBGC)
+       $(LIBPCRE) $(LIBDB) $(LIBICONV) $(LIBGC) $(LIBGCRYPT)
 disorder_dump_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a
 
 disorder_cgi_SOURCES=dcgi.c dcgi.h                     \
 disorder_dump_DEPENDENCIES=$(LIBOBJS) ../lib/libdisorder.a
 
 disorder_cgi_SOURCES=dcgi.c dcgi.h                     \
@@ -86,7 +87,7 @@ disorder_cgi_LDFLAGS=-export-dynamic
 disorder_cgi_DEPENDENCIES=../lib/libdisorder.a
 
 trackname_SOURCES=trackname.c
 disorder_cgi_DEPENDENCIES=../lib/libdisorder.a
 
 trackname_SOURCES=trackname.c
-trackname_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBICONV)
+trackname_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
 trackname_DEPENDENCIES=../lib/libdisorder.a
 
 install-exec-hook:
 trackname_DEPENDENCIES=../lib/libdisorder.a
 
 install-exec-hook:
index a30e97828b4f30a1e9a646171a7464c2d591b0c5..6ee11c962c221daa067e0fa53563fa62a547a6cb 100644 (file)
@@ -391,7 +391,8 @@ static int c_user(struct conn *c,
     sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
     return 1;
   }
     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]);
+  res = authhash(c->nonce, sizeof c->nonce, config->allow.s[n].s[1],
+                config->authorization_algorithm);
   if(wideopen || (res && !strcmp(res, vec[1]))) {
     c->who = vec[0];
     /* currently we only bother logging remote connections */
   if(wideopen || (res && !strcmp(res, vec[1]))) {
     c->who = vec[0];
     /* currently we only bother logging remote connections */
@@ -1084,7 +1085,15 @@ static int listen_callback(ev_source *ev,
   c->reader = reader_callback;
   c->l = l;
   gcry_randomize(c->nonce, sizeof c->nonce, GCRY_STRONG_RANDOM);
   c->reader = reader_callback;
   c->l = l;
   gcry_randomize(c->nonce, sizeof c->nonce, GCRY_STRONG_RANDOM);
-  sink_printf(ev_writer_sink(c->w), "231 %s\n", hex(c->nonce, sizeof c->nonce));
+  if(!strcmp(config->authorization_algorithm, "sha1")
+     || !strcmp(config->authorization_algorithm, "SHA1")) {
+    sink_printf(ev_writer_sink(c->w), "231 %s\n",
+               hex(c->nonce, sizeof c->nonce));
+  } else {
+    sink_printf(ev_writer_sink(c->w), "231 %s %s\n",
+               config->authorization_algorithm,
+               hex(c->nonce, sizeof c->nonce));
+  }
   return 0;
 }
 
   return 0;
 }