Subversion Repositories

?revision_form?Rev ?revision_input??revision_submit??revision_endform?

Rev 79 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

Author: Magnus Holmgren <holmgren@debian.org>
Description: Check keys against openssh-blacklist
 Check keys before accepting for pubkey authentication as well as on conversion
 by lsh-writekey and lsh-decode-key.
 .
 blacklist.c code copied from the openssh package and adapted for LSH.

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -69,7 +69,8 @@ liblsh_a_SOURCES = abstract_io.c abstrac
        unix_interact.c unix_process.c unix_random.c unix_user.c \
        userauth.c \
        werror.c write_buffer.c write_packet.c \
-       xalloc.c xauth.c zlib.c
+       xalloc.c xauth.c zlib.c \
+       blacklist.c
 
 liblsh_a_LIBADD = @LIBOBJS@
 
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -94,7 +94,8 @@ am_liblsh_a_OBJECTS = abstract_io.$(OBJE
        tty.$(OBJEXT) unix_interact.$(OBJEXT) unix_process.$(OBJEXT) \
        unix_random.$(OBJEXT) unix_user.$(OBJEXT) userauth.$(OBJEXT) \
        werror.$(OBJEXT) write_buffer.$(OBJEXT) write_packet.$(OBJEXT) \
-       xalloc.$(OBJEXT) xauth.$(OBJEXT) zlib.$(OBJEXT)
+       xalloc.$(OBJEXT) xauth.$(OBJEXT) zlib.$(OBJEXT) \
+       blacklist.$(OBJEXT)
 liblsh_a_OBJECTS = $(am_liblsh_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \
        "$(DESTDIR)$(bindir)"
@@ -554,7 +555,8 @@ liblsh_a_SOURCES = abstract_io.c abstrac
        unix_interact.c unix_process.c unix_random.c unix_user.c \
        userauth.c \
        werror.c write_buffer.c write_packet.c \
-       xalloc.c xauth.c zlib.c
+       xalloc.c xauth.c zlib.c \
+       blacklist.c
 
 liblsh_a_LIBADD = @LIBOBJS@
 
@@ -793,6 +795,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/algorithms.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alist.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atoms.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blacklist.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel_commands.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel_forward.Po@am__quote@
--- a/src/abstract_crypto.h
+++ b/src/abstract_crypto.h
@@ -162,7 +162,9 @@ MAC_DIGEST((instance), lsh_string_alloc(
        (public_key method (string))
       
        ; Returns (public-key (<pub-sig-alg-id> <s-expr>*))
-       (public_spki_key method (string) "int transport")))
+       (public_spki_key method (string) "int transport")
+
+       (key_size method uint32_t)))
 */
 
 #define VERIFY(verifier, algorithm, length, data, slength, sdata) \
@@ -170,7 +172,7 @@ MAC_DIGEST((instance), lsh_string_alloc(
 
 #define PUBLIC_KEY(verifier) ((verifier)->public_key((verifier)))
 #define PUBLIC_SPKI_KEY(verifier, t) ((verifier)->public_spki_key((verifier), (t)))
-
+#define KEY_SIZE(verifier) ((verifier)->key_size((verifier)))
 
 /* GABA:
    (class
--- a/src/abstract_crypto.h.x
+++ b/src/abstract_crypto.h.x
@@ -161,6 +161,7 @@ struct verifier
   int (*(verify))(struct verifier *self,int algorithm,uint32_t length,const uint8_t *data,uint32_t signature_length,const uint8_t *signature_data);
   struct lsh_string *(*(public_key))(struct verifier *self);
   struct lsh_string *(*(public_spki_key))(struct verifier *self,int transport);
+  uint32_t *(*(key_size))(struct verifier *self);
 };
 extern struct lsh_class verifier_class;
 #endif /* !GABA_DEFINE */
--- /dev/null
+++ b/src/blacklist.c
@@ -0,0 +1,152 @@
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#include "atoms.h"
+#include "format.h"
+#include "lsh_string.h"
+#include "werror.h"
+#include "crypto.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+int blacklisted_key(struct verifier *v, int method);
+
+/* Scan a blacklist of known-vulnerable keys in blacklist_file. */
+static int
+blacklisted_key_in_file(struct lsh_string *lsh_hash, struct lsh_string *blacklist_file)
+{
+  int fd = -1;
+  const char *hash = 0;
+  uint32_t line_len;
+  struct stat st;
+  char buf[256];
+  off_t start, lower, upper;
+  int ret = 0;
+
+  debug("Checking blacklist file %S\n", blacklist_file);
+  fd = open(lsh_get_cstring(blacklist_file), O_RDONLY);
+  if (fd < 0) {
+    ret = -1;
+    goto out;
+  }
+
+  hash = lsh_get_cstring(lsh_hash) + 12;
+  line_len = strlen(hash);
+  if (line_len != 20)
+    goto out;
+
+  /* Skip leading comments */
+  start = 0;
+  for (;;) {
+    ssize_t r;
+    char *newline;
+
+    r = read(fd, buf, sizeof(buf));
+    if (r <= 0)
+      goto out;
+    if (buf[0] != '#')
+      break;
+
+    newline = memchr(buf, '\n', sizeof(buf));
+    if (!newline)
+      goto out;
+    start += newline + 1 - buf;
+    if (lseek(fd, start, SEEK_SET) < 0)
+      goto out;
+  }
+
+  /* Initialise binary search record numbers */
+  if (fstat(fd, &st) < 0)
+    goto out;
+  lower = 0;
+  upper = (st.st_size - start) / (line_len + 1);
+
+  while (lower != upper) {
+    off_t cur;
+    int cmp;
+
+    cur = lower + (upper - lower) / 2;
+
+    /* Read this line and compare to digest; this is
+     * overflow-safe since cur < max(off_t) / (line_len + 1) */
+    if (lseek(fd, start + cur * (line_len + 1), SEEK_SET) < 0)
+      break;
+    if (read(fd, buf, line_len) != line_len)
+      break;
+    cmp = memcmp(buf, hash, line_len);
+    if (cmp < 0) {
+      if (cur == lower)
+       break;
+      lower = cur;
+    } else if (cmp > 0) {
+      if (cur == upper)
+       break;
+      upper = cur;
+    } else {
+      ret = 1;
+      break;
+    }
+  }
+
+out:
+  if (fd >= 0)
+    close(fd);
+  return ret;
+}
+
+/*
+ * Scan blacklists of known-vulnerable keys. If a vulnerable key is found,
+ * its fingerprint is returned in *fp, unless fp is NULL.
+ */
+int
+blacklisted_key(struct verifier *v, int method)
+{
+    const char *keytype;
+    int ret = -1;
+    const char *paths[] = { "/usr/share/ssh/blacklist", "/etc/ssh/blacklist", NULL };
+    const char **pp;
+    struct lsh_string *lsh_hash = ssh_format("%lfxS",
+                                            hash_string(&crypto_md5_algorithm,
+                                                        PUBLIC_KEY(v), 1));
+    uint32_t keysize = KEY_SIZE(v);
+
+    switch (method)
+      {
+      case ATOM_SSH_DSS:
+      case ATOM_DSA:
+         keytype = "DSA";
+         break;
+      case ATOM_SSH_RSA:
+      case ATOM_RSA_PKCS1_SHA1:
+      case ATOM_RSA_PKCS1_MD5:
+      case ATOM_RSA_PKCS1:
+         keytype = "RSA";
+         break;
+      default:
+         werror("Unrecognized key type");
+         return -1;
+      }
+
+    for (pp = paths; *pp && ret <= 0; pp++) {
+      struct lsh_string *blacklist_file = ssh_format("%lz.%lz-%di",
+                                                    *pp, keytype, keysize);
+      int r = blacklisted_key_in_file(lsh_hash, blacklist_file);
+      lsh_string_free(blacklist_file);
+      if (r > ret) ret = r;
+    }
+
+    if (ret > 0) {
+       werror("Key is compromised: %z %i %fS\n", keytype, keysize,
+              lsh_string_colonize(lsh_hash, 2, 0));
+    } else if (ret < 0) {
+       verbose("No blacklist for key type %z size %i", keytype, keysize);
+    }
+    return ret;
+}
--- a/src/dsa.c
+++ b/src/dsa.c
@@ -189,6 +189,14 @@ do_dsa_public_spki_key(struct verifier *
                                "y", self->key.y);
 }
 
+static uint32_t
+do_dsa_key_size(struct verifier *v)
+{
+  CAST(dsa_verifier, self, v);
+
+  return mpz_sizeinbase(self->key.p, 2);
+}
+
 static void
 init_dsa_verifier(struct dsa_verifier *self)
 {
@@ -199,6 +207,7 @@ init_dsa_verifier(struct dsa_verifier *s
   self->super.verify = do_dsa_verify;
   self->super.public_spki_key = do_dsa_public_spki_key;
   self->super.public_key = do_dsa_public_key;
+  self->super.key_size = do_dsa_key_size;
 }
 
 
--- a/src/lsh-decode-key.c
+++ b/src/lsh-decode-key.c
@@ -133,6 +133,10 @@ lsh_decode_key(struct lsh_string *conten
             werror("Invalid dsa key.\n");
             return NULL;
           }
+        else if (blacklisted_key(v, type) > 0)
+          {
+            return NULL;
+          }
         else
           return PUBLIC_SPKI_KEY(v, 1);
       }
@@ -150,6 +154,10 @@ lsh_decode_key(struct lsh_string *conten
               werror("Invalid rsa key.\n");
               return NULL;
             }
+          else if (blacklisted_key(v, type) > 0)
+            {
+              return NULL;
+            }
           else
             return PUBLIC_SPKI_KEY(v, 1);
       }      
--- a/src/lsh-writekey.c
+++ b/src/lsh-writekey.c
@@ -397,14 +397,18 @@ process_public(const struct lsh_string *
 {
   struct signer *s;
   struct verifier *v;
+  int algorithm_name;
   
-  s = spki_make_signer(options->signature_algorithms, key, NULL);
+  s = spki_make_signer(options->signature_algorithms, key, &algorithm_name);
   
   if (!s)
     return NULL;
 
   v = SIGNER_GET_VERIFIER(s);
   assert(v);
+  if (blacklisted_key(v, algorithm_name) > 0) {
+    return NULL;
+  }
 
   return PUBLIC_SPKI_KEY(v, 1);
 }
@@ -416,7 +420,8 @@ main(int argc, char **argv)
   int private_fd;
   int public_fd;
   struct lsh_string *input;
-  struct lsh_string *output;
+  struct lsh_string *priv_output;
+  struct lsh_string *pub_output;
   const struct exception *e;
 
   argp_parse(&main_argp, argc, argv, 0, NULL, options);
@@ -439,16 +444,22 @@ main(int argc, char **argv)
       return EXIT_FAILURE;
     }
   
-  output = process_private(input, options);
-  if (!output)
+  pub_output = process_public(input, options);
+  if (!pub_output)
+    return EXIT_FAILURE;
+
+  priv_output = process_private(input, options);
+  if (!priv_output)
     return EXIT_FAILURE;
 
+  lsh_string_free(input);
+
   private_fd = open_file(options->private_file);
   if (private_fd < 0)
     return EXIT_FAILURE;
 
-  e = write_raw(private_fd, STRING_LD(output));
-  lsh_string_free(output);
+  e = write_raw(private_fd, STRING_LD(priv_output));
+  lsh_string_free(priv_output);
 
   if (e)
     {
@@ -457,18 +468,12 @@ main(int argc, char **argv)
       return EXIT_FAILURE;
     }
 
-  output = process_public(input, options);
-  lsh_string_free(input);
-  
-  if (!output)
-    return EXIT_FAILURE;
-
   public_fd = open_file(options->public_file);
   if (public_fd < 0)
     return EXIT_FAILURE;
   
-  e = write_raw(public_fd, STRING_LD(output));
-  lsh_string_free(output);
+  e = write_raw(public_fd, STRING_LD(pub_output));
+  lsh_string_free(pub_output);
   
   if (e)
     {
--- a/src/publickey_crypto.h
+++ b/src/publickey_crypto.h
@@ -203,5 +203,7 @@ parse_ssh_dss_public(struct simple_buffe
 struct verifier *
 make_ssh_dss_verifier(const struct lsh_string *public);
 
+int
+blacklisted_key(struct verifier *v, int method);
 
 #endif /* LSH_PUBLICKEY_CRYPTO_H_INCLUDED */
--- a/src/rsa.c
+++ b/src/rsa.c
@@ -167,6 +167,14 @@ do_rsa_public_spki_key(struct verifier *
                                self->key.n, self->key.e);
 }
 
+static uint32_t
+do_rsa_key_size(struct verifier *v)
+{
+  CAST(rsa_verifier, self, v);
+
+  return mpz_sizeinbase(self->key.n, 2);
+}
+
 
 /* NOTE: To initialize an rsa verifier, one must
  *
@@ -184,6 +192,7 @@ init_rsa_verifier(struct rsa_verifier *s
   self->super.verify = do_rsa_verify;
   self->super.public_key = do_rsa_public_key;
   self->super.public_spki_key = do_rsa_public_spki_key;
+  self->super.key_size = do_rsa_key_size;
 }
 
 /* Alternative constructor using a key of type ssh-rsa, when the atom
--- a/src/server_authorization.c
+++ b/src/server_authorization.c
@@ -93,7 +93,8 @@ do_key_lookup(struct lookup_verifier *c,
                                    PUBLIC_SPKI_KEY(v, 0),
                                    1));
   
-  if (USER_FILE_EXISTS(keyholder, filename, 1))
+  if (USER_FILE_EXISTS(keyholder, filename, 1)
+      && blacklisted_key(v, method) < 1)
     return v;
 
   return NULL;