initial support for DSA keys. ok deraadt@, niels@
authormarkus <markus@openbsd.org>
Thu, 23 Mar 2000 22:15:33 +0000 (22:15 +0000)
committermarkus <markus@openbsd.org>
Thu, 23 Mar 2000 22:15:33 +0000 (22:15 +0000)
12 files changed:
usr.bin/ssh/auth-rh-rsa.c
usr.bin/ssh/auth-rsa.c
usr.bin/ssh/hostfile.c
usr.bin/ssh/hostfile.h [new file with mode: 0644]
usr.bin/ssh/key.c [new file with mode: 0644]
usr.bin/ssh/key.h [new file with mode: 0644]
usr.bin/ssh/lib/Makefile
usr.bin/ssh/match.c
usr.bin/ssh/match.h [new file with mode: 0644]
usr.bin/ssh/ssh.h
usr.bin/ssh/sshconnect.c
usr.bin/ssh/sshd.c

index a9195f0..b7adab7 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: auth-rh-rsa.c,v 1.10 1999/11/24 19:53:43 markus Exp $");
+RCSID("$Id: auth-rh-rsa.c,v 1.11 2000/03/23 22:15:33 markus Exp $");
 
 #include "packet.h"
 #include "ssh.h"
@@ -23,37 +23,46 @@ RCSID("$Id: auth-rh-rsa.c,v 1.10 1999/11/24 19:53:43 markus Exp $");
 #include "uidswap.h"
 #include "servconf.h"
 
+#include <ssl/rsa.h>
+#include <ssl/dsa.h>
+#include "key.h"
+#include "hostfile.h"
+
 /*
  * Tries to authenticate the user using the .rhosts file and the host using
  * its host key.  Returns true if authentication succeeds.
  */
 
 int 
-auth_rhosts_rsa(struct passwd *pw, const char *client_user,
-               BIGNUM *client_host_key_e, BIGNUM *client_host_key_n)
+auth_rhosts_rsa(struct passwd *pw, const char *client_user, RSA *client_host_key)
 {
        extern ServerOptions options;
        const char *canonical_hostname;
        HostStatus host_status;
-       BIGNUM *ke, *kn;
+       Key *client_key, *found;
 
        debug("Trying rhosts with RSA host authentication for %.100s", client_user);
 
+       if (client_host_key == NULL)
+               return 0;
+
        /* Check if we would accept it using rhosts authentication. */
        if (!auth_rhosts(pw, client_user))
                return 0;
 
        canonical_hostname = get_canonical_hostname();
 
-       debug("Rhosts RSA authentication: canonical host %.900s",
-             canonical_hostname);
+       debug("Rhosts RSA authentication: canonical host %.900s", canonical_hostname);
+
+       /* wrap the RSA key into a 'generic' key */
+       client_key = key_new(KEY_RSA);
+       BN_copy(client_key->rsa->e, client_host_key->e);
+       BN_copy(client_key->rsa->n, client_host_key->n);
+       found = key_new(KEY_RSA);
 
        /* Check if we know the host and its host key. */
-       ke = BN_new();
-       kn = BN_new();
        host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname,
-                                            client_host_key_e, client_host_key_n,
-                                            ke, kn);
+           client_key, found);
 
        /* Check user host file unless ignored. */
        if (host_status != HOST_OK && !options.ignore_user_known_hosts) {
@@ -73,14 +82,13 @@ auth_rhosts_rsa(struct passwd *pw, const char *client_user,
                        /* XXX race between stat and the following open() */
                        temporarily_use_uid(pw->pw_uid);
                        host_status = check_host_in_hostfile(user_hostfile, canonical_hostname,
-                                                            client_host_key_e, client_host_key_n,
-                                                            ke, kn);
+                           client_key, found);
                        restore_uid();
                }
                xfree(user_hostfile);
        }
-       BN_free(ke);
-       BN_free(kn);
+       key_free(client_key);
+       key_free(found);
 
        if (host_status != HOST_OK) {
                debug("Rhosts with RSA host authentication denied: unknown or invalid host key");
@@ -90,7 +98,7 @@ auth_rhosts_rsa(struct passwd *pw, const char *client_user,
        /* A matching host key was found and is known. */
 
        /* Perform the challenge-response dialog with the client for the host key. */
-       if (!auth_rsa_challenge_dialog(client_host_key_e, client_host_key_n)) {
+       if (!auth_rsa_challenge_dialog(client_host_key)) {
                log("Client on %.800s failed to respond correctly to host authentication.",
                    canonical_hostname);
                return 0;
@@ -101,7 +109,7 @@ auth_rhosts_rsa(struct passwd *pw, const char *client_user,
         */
 
        verbose("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.",
-               pw->pw_name, client_user, canonical_hostname);
+          pw->pw_name, client_user, canonical_hostname);
        packet_send_debug("Rhosts with RSA host authentication accepted.");
        return 1;
 }
index a04adf6..3d2e84f 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: auth-rsa.c,v 1.18 2000/02/11 10:59:11 markus Exp $");
+RCSID("$Id: auth-rsa.c,v 1.19 2000/03/23 22:15:33 markus Exp $");
 
 #include "rsa.h"
 #include "packet.h"
@@ -24,6 +24,7 @@ RCSID("$Id: auth-rsa.c,v 1.18 2000/02/11 10:59:11 markus Exp $");
 #include "ssh.h"
 #include "mpaux.h"
 #include "uidswap.h"
+#include "match.h"
 #include "servconf.h"
 
 #include <ssl/rsa.h>
@@ -60,10 +61,9 @@ extern unsigned char session_id[16];
  */
 
 int
-auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n)
+auth_rsa_challenge_dialog(RSA *pk)
 {
        BIGNUM *challenge, *encrypted_challenge;
-       RSA *pk;
        BN_CTX *ctx;
        unsigned char buf[32], mdbuf[16], response[16];
        MD5_CTX md;
@@ -76,19 +76,11 @@ auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n)
        /* Generate a random challenge. */
        BN_rand(challenge, 256, 0, 0);
        ctx = BN_CTX_new();
-       BN_mod(challenge, challenge, n, ctx);
+       BN_mod(challenge, challenge, pk->n, ctx);
        BN_CTX_free(ctx);
 
-       /* Create the public key data structure. */
-       pk = RSA_new();
-       pk->e = BN_new();
-       BN_copy(pk->e, e);
-       pk->n = BN_new();
-       BN_copy(pk->n, n);
-
        /* Encrypt the challenge with the public key. */
        rsa_public_encrypt(encrypted_challenge, challenge, pk);
-       RSA_free(pk);
 
        /* Send the encrypted challenge to the client. */
        packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
@@ -140,7 +132,7 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
        FILE *f;
        unsigned long linenum = 0;
        struct stat st;
-       BIGNUM *e, *n;
+       RSA *pk;
 
        /* Temporarily use the user's uid. */
        temporarily_use_uid(pw->pw_uid);
@@ -202,8 +194,9 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
        /* Flag indicating whether authentication has succeeded. */
        authenticated = 0;
 
-       e = BN_new();
-       n = BN_new();
+       pk = RSA_new();
+       pk->e = BN_new();
+       pk->n = BN_new();
 
        /*
         * Go though the accepted keys, looking for the current key.  If
@@ -241,7 +234,7 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
                        options = NULL;
 
                /* Parse the key from the line. */
-               if (!auth_rsa_read_key(&cp, &bits, e, n)) {
+               if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) {
                        debug("%.100s, line %lu: bad key syntax",
                              SSH_USER_PERMITTED_KEYS, linenum);
                        packet_send_debug("%.100s, line %lu: bad key syntax",
@@ -251,19 +244,20 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
                /* cp now points to the comment part. */
 
                /* Check if the we have found the desired key (identified by its modulus). */
-               if (BN_cmp(n, client_n) != 0)
+               if (BN_cmp(pk->n, client_n) != 0)
                        continue;
 
                /* check the real bits  */
-               if (bits != BN_num_bits(n))
+               if (bits != BN_num_bits(pk->n))
                        log("Warning: %s, line %ld: keysize mismatch: "
                            "actual %d vs. announced %d.",
-                           file, linenum, BN_num_bits(n), bits);
+                           file, linenum, BN_num_bits(pk->n), bits);
 
                /* We have found the desired key. */
 
+
                /* Perform the challenge-response dialog for this key. */
-               if (!auth_rsa_challenge_dialog(e, n)) {
+               if (!auth_rsa_challenge_dialog(pk)) {
                        /* Wrong response. */
                        verbose("Wrong response to RSA authentication challenge.");
                        packet_send_debug("Wrong response to RSA authentication challenge.");
@@ -466,8 +460,7 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
        /* Close the file. */
        fclose(f);
 
-       BN_clear_free(n);
-       BN_clear_free(e);
+       RSA_free(pk);
 
        if (authenticated)
                packet_send_debug("RSA authentication accepted.");
index ea92fa0..eca68da 100644 (file)
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: hostfile.c,v 1.13 2000/02/18 10:20:20 markus Exp $");
+RCSID("$OpenBSD: hostfile.c,v 1.14 2000/03/23 22:15:33 markus Exp $");
 
 #include "packet.h"
+#include "match.h"
 #include "ssh.h"
+#include <ssl/rsa.h>
+#include <ssl/dsa.h>
+#include "key.h"
+#include "hostfile.h"
 
 /*
- * Reads a multiple-precision integer in decimal from the buffer, and advances
- * the pointer.  The integer must already be initialized.  This function is
- * permitted to modify the buffer.  This leaves *cpp to point just beyond the
- * last processed (and maybe modified) character.  Note that this may modify
- * the buffer containing the number.
+ * Parses an RSA (number of bits, e, n) or DSA key from a string.  Moves the
+ * pointer over the key.  Skips any whitespace at the beginning and at end.
  */
 
 int
-auth_rsa_read_bignum(char **cpp, BIGNUM * value)
-{
-       char *cp = *cpp;
-       int old;
-
-       /* Skip any leading whitespace. */
-       for (; *cp == ' ' || *cp == '\t'; cp++)
-               ;
-
-       /* Check that it begins with a decimal digit. */
-       if (*cp < '0' || *cp > '9')
-               return 0;
-
-       /* Save starting position. */
-       *cpp = cp;
-
-       /* Move forward until all decimal digits skipped. */
-       for (; *cp >= '0' && *cp <= '9'; cp++)
-               ;
-
-       /* Save the old terminating character, and replace it by \0. */
-       old = *cp;
-       *cp = 0;
-
-       /* Parse the number. */
-       if (BN_dec2bn(&value, *cpp) == 0)
-               return 0;
-
-       /* Restore old terminating character. */
-       *cp = old;
-
-       /* Move beyond the number and return success. */
-       *cpp = cp;
-       return 1;
-}
-
-/*
- * Parses an RSA key (number of bits, e, n) from a string.  Moves the pointer
- * over the key.  Skips any whitespace at the beginning and at end.
- */
-
-int
-auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
+hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret)
 {
        unsigned int bits;
        char *cp;
@@ -85,12 +45,7 @@ auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
        for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
                bits = 10 * bits + *cp - '0';
 
-       /* Get public exponent. */
-       if (!auth_rsa_read_bignum(&cp, e))
-               return 0;
-
-       /* Get public modulus. */
-       if (!auth_rsa_read_bignum(&cp, n))
+       if (!key_read(ret, bits, &cp))
                return 0;
 
        /* Skip trailing whitespace. */
@@ -103,63 +58,30 @@ auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
        return 1;
 }
 
-/*
- * Tries to match the host name (which must be in all lowercase) against the
- * comma-separated sequence of subpatterns (each possibly preceded by ! to
- * indicate negation).  Returns true if there is a positive match; zero
- * otherwise.
- */
-
 int
-match_hostname(const char *host, const char *pattern, unsigned int len)
+auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
 {
-       char sub[1024];
-       int negated;
-       int got_positive;
-       unsigned int i, subi;
-
-       got_positive = 0;
-       for (i = 0; i < len;) {
-               /* Check if the subpattern is negated. */
-               if (pattern[i] == '!') {
-                       negated = 1;
-                       i++;
-               } else
-                       negated = 0;
-
-               /*
-                * Extract the subpattern up to a comma or end.  Convert the
-                * subpattern to lowercase.
-                */
-               for (subi = 0;
-                    i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
-                    subi++, i++)
-                       sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
-               /* If subpattern too long, return failure (no match). */
-               if (subi >= sizeof(sub) - 1)
-                       return 0;
-
-               /* If the subpattern was terminated by a comma, skip the comma. */
-               if (i < len && pattern[i] == ',')
-                       i++;
-
-               /* Null-terminate the subpattern. */
-               sub[subi] = '\0';
+       Key *k = key_new(KEY_RSA);
+       int ret = hostfile_read_key(cpp, bitsp, k);
+       BN_copy(e, k->rsa->e);
+       BN_copy(n, k->rsa->n);
+       key_free(k);
+       return ret;
+}
 
-               /* Try to match the subpattern against the host name. */
-               if (match_pattern(host, sub)) {
-                       if (negated)
-                               return 0;       /* Fail */
-                       else
-                               got_positive = 1;
-               }
+int
+hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum)
+{
+       if (key == NULL || key->type != KEY_RSA || key->rsa == NULL)
+               return 1;
+       if (bits != BN_num_bits(key->rsa->n)) {
+               error("Warning: %s, line %d: keysize mismatch for host %s: "
+                   "actual %d vs. announced %d.",
+                   filename, linenum, host, BN_num_bits(key->rsa->n), bits);
+               error("Warning: replace %d with %d in %s, line %d.",
+                   bits, BN_num_bits(key->rsa->n), filename, linenum);
        }
-
-       /*
-        * Return success if got a positive match.  If there was a negative
-        * match, we have already returned zero and never get here.
-        */
-       return got_positive;
+       return 1;
 }
 
 /*
@@ -170,8 +92,7 @@ match_hostname(const char *host, const char *pattern, unsigned int len)
  */
 
 HostStatus
-check_host_in_hostfile(const char *filename, const char *host,
-                      BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn)
+check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found)
 {
        FILE *f;
        char line[8192];
@@ -180,6 +101,8 @@ check_host_in_hostfile(const char *filename, const char *host,
        char *cp, *cp2;
        HostStatus end_return;
 
+       if (key == NULL)
+               fatal("no key to look up");
        /* Open the file containing the list of known hosts. */
        f = fopen(filename, "r");
        if (!f)
@@ -221,18 +144,13 @@ check_host_in_hostfile(const char *filename, const char *host,
                 * Extract the key from the line.  This will skip any leading
                 * whitespace.  Ignore badly formatted lines.
                 */
-               if (!auth_rsa_read_key(&cp, &kbits, ke, kn))
+               if (!hostfile_read_key(&cp, &kbits, found))
+                       continue;
+               if (!hostfile_check_key(kbits, found, host, filename, linenum))
                        continue;
 
-               if (kbits != BN_num_bits(kn)) {
-                       error("Warning: %s, line %d: keysize mismatch for host %s: "
-                             "actual %d vs. announced %d.",
-                             filename, linenum, host, BN_num_bits(kn), kbits);
-                       error("Warning: replace %d with %d in %s, line %d.",
-                             kbits, BN_num_bits(kn), filename, linenum);
-               }
                /* Check if the current key is the same as the given key. */
-               if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) {
+               if (key_equal(key, found)) {
                        /* Ok, they match. */
                        fclose(f);
                        return HOST_OK;
@@ -260,41 +178,28 @@ check_host_in_hostfile(const char *filename, const char *host,
  */
 
 int
-add_host_to_hostfile(const char *filename, const char *host,
-                    BIGNUM * e, BIGNUM * n)
+add_host_to_hostfile(const char *filename, const char *host, Key *key)
 {
        FILE *f;
-       char *buf;
-       unsigned int bits;
+       int success = 0;
+
+       if (key == NULL)
+               return 1;
 
        /* Open the file for appending. */
        f = fopen(filename, "a");
        if (!f)
                return 0;
 
-       /* size of modulus 'n' */
-       bits = BN_num_bits(n);
-
-       /* Print the host name and key to the file. */
-       fprintf(f, "%s %u ", host, bits);
-       buf = BN_bn2dec(e);
-       if (buf == NULL) {
-               error("add_host_to_hostfile: BN_bn2dec(e) failed");
-               fclose(f);
-               return 0;
+       fprintf(f, "%s ", host);
+       if (key_write(key, f)) {
+               fprintf(f, "\n");
+               success = 1;
+       } else {
+               error("add_host_to_hostfile: saving key failed");
        }
-       fprintf(f, "%s ", buf);
-       free(buf);
-       buf = BN_bn2dec(n);
-       if (buf == NULL) {
-               error("add_host_to_hostfile: BN_bn2dec(n) failed");
-               fclose(f);
-               return 0;
-       }
-       fprintf(f, "%s\n", buf);
-       free(buf);
 
        /* Close the file. */
        fclose(f);
-       return 1;
+       return success;
 }
diff --git a/usr.bin/ssh/hostfile.h b/usr.bin/ssh/hostfile.h
new file mode 100644 (file)
index 0000000..64fe185
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef HOSTFILE_H
+#define HOSTFILE_H
+
+/*
+ * Checks whether the given host is already in the list of our known hosts.
+ * Returns HOST_OK if the host is known and has the specified key, HOST_NEW
+ * if the host is not known, and HOST_CHANGED if the host is known but used
+ * to have a different host key.  The host must be in all lowercase.
+ */
+typedef enum {
+       HOST_OK, HOST_NEW, HOST_CHANGED
+}       HostStatus;
+HostStatus 
+check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found);
+
+/*
+ * Appends an entry to the host file.  Returns false if the entry could not
+ * be appended.
+ */
+int    add_host_to_hostfile(const char *filename, const char *host, Key *key);
+
+#endif
diff --git a/usr.bin/ssh/key.c b/usr.bin/ssh/key.c
new file mode 100644 (file)
index 0000000..6ad35cb
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Markus Friedl.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * read_bignum():
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ */
+
+#include "includes.h"
+#include "ssh.h"
+#include <ssl/rsa.h>
+#include <ssl/dsa.h>
+#include <ssl/evp.h>
+#include "xmalloc.h"
+#include "key.h"
+
+Key *
+key_new(int type)
+{
+       Key *k;
+       RSA *rsa;
+       DSA *dsa;
+       k = xmalloc(sizeof(*k));
+       k->type = type;
+       switch (k->type) {
+       case KEY_RSA:
+               rsa = RSA_new();
+               rsa->n = BN_new();
+               rsa->e = BN_new();
+               k->rsa = rsa;
+               break;
+       case KEY_DSA:
+               dsa = DSA_new();
+               dsa->p = BN_new();
+               dsa->q = BN_new();
+               dsa->g = BN_new();
+               dsa->pub_key = BN_new();
+               k->dsa = dsa;
+               break;
+       case KEY_EMPTY:
+               k->dsa = NULL;
+               k->rsa = NULL;
+               break;
+       default:
+               fatal("key_new: bad key type %d", k->type);
+               break;
+       }
+       return k;
+}
+void
+key_free(Key *k)
+{
+       switch (k->type) {
+       case KEY_RSA:
+               if (k->rsa != NULL)
+                       RSA_free(k->rsa);
+               k->rsa = NULL;
+               break;
+       case KEY_DSA:
+               if (k->dsa != NULL)
+                       DSA_free(k->dsa);
+               k->dsa = NULL;
+               break;
+       default:
+               fatal("key_free: bad key type %d", k->type);
+               break;
+       }
+       xfree(k);
+}
+int
+key_equal(Key *a, Key *b)
+{
+       if (a == NULL || b == NULL || a->type != b->type)
+               return 0;
+       switch (a->type) {
+       case KEY_RSA:
+               return a->rsa != NULL && b->rsa != NULL &&
+                   BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
+                   BN_cmp(a->rsa->n, b->rsa->n) == 0;
+               break;
+       case KEY_DSA:
+               return a->dsa != NULL && b->dsa != NULL &&
+                   BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
+                   BN_cmp(a->dsa->q, b->dsa->q) == 0 &&
+                   BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
+                   BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
+               break;
+       default:
+               fatal("key_free: bad key type %d", a->type);
+               break;
+       }
+       return 0;
+}
+
+#define FPRINT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Generate key fingerprint in ascii format.
+ * Based on ideas and code from Bjoern Groenvall <bg@sics.se>
+ */
+char *
+key_fingerprint(Key *k)
+{
+       static char retval[80];
+       unsigned char *buf = NULL;
+       int len = 0;
+       int nlen, elen, plen, qlen, glen, publen;
+
+       switch (k->type) {
+       case KEY_RSA:
+               nlen = BN_num_bytes(k->rsa->n);
+               elen = BN_num_bytes(k->rsa->e);
+               len = nlen + elen;
+               buf = xmalloc(len);
+               BN_bn2bin(k->rsa->n, buf);
+               BN_bn2bin(k->rsa->e, buf + nlen);
+               break;
+       case KEY_DSA:
+               plen = BN_num_bytes(k->dsa->p);
+               qlen = BN_num_bytes(k->dsa->q);
+               glen = BN_num_bytes(k->dsa->g);
+               publen = BN_num_bytes(k->dsa->pub_key);
+               len = qlen + qlen + glen + publen;
+               buf = xmalloc(len);
+               BN_bn2bin(k->dsa->p, buf);
+               BN_bn2bin(k->dsa->q, buf + plen);
+               BN_bn2bin(k->dsa->g, buf + plen + qlen);
+               BN_bn2bin(k->dsa->pub_key , buf + plen + qlen + glen);
+               break;
+       default:
+               fatal("key_fingerprint: bad key type %d", k->type);
+               break;
+       }
+       if (buf != NULL) {
+               unsigned char d[16];
+               EVP_MD_CTX md;
+               EVP_DigestInit(&md, EVP_md5());
+               EVP_DigestUpdate(&md, buf, len);
+               EVP_DigestFinal(&md, d, NULL);
+               snprintf(retval, sizeof(retval), FPRINT,
+                   d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
+                   d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+               memset(buf, 0, len);
+               xfree(buf);
+       }
+       return retval;
+}
+
+/*
+ * Reads a multiple-precision integer in decimal from the buffer, and advances
+ * the pointer.  The integer must already be initialized.  This function is
+ * permitted to modify the buffer.  This leaves *cpp to point just beyond the
+ * last processed (and maybe modified) character.  Note that this may modify
+ * the buffer containing the number.
+ */
+int
+read_bignum(char **cpp, BIGNUM * value)
+{
+       char *cp = *cpp;
+       int old;
+
+       /* Skip any leading whitespace. */
+       for (; *cp == ' ' || *cp == '\t'; cp++)
+               ;
+
+       /* Check that it begins with a decimal digit. */
+       if (*cp < '0' || *cp > '9')
+               return 0;
+
+       /* Save starting position. */
+       *cpp = cp;
+
+       /* Move forward until all decimal digits skipped. */
+       for (; *cp >= '0' && *cp <= '9'; cp++)
+               ;
+
+       /* Save the old terminating character, and replace it by \0. */
+       old = *cp;
+       *cp = 0;
+
+       /* Parse the number. */
+       if (BN_dec2bn(&value, *cpp) == 0)
+               return 0;
+
+       /* Restore old terminating character. */
+       *cp = old;
+
+       /* Move beyond the number and return success. */
+       *cpp = cp;
+       return 1;
+}
+int
+write_bignum(FILE *f, BIGNUM *num)
+{
+       char *buf = BN_bn2dec(num);
+       if (buf == NULL) {
+               error("write_bignum: BN_bn2dec() failed");
+               return 0;
+       }
+       fprintf(f, " %s", buf);
+       free(buf);
+       return 1;
+}
+int
+key_read(Key *ret, unsigned int bits, char **cpp)
+{
+       switch(ret->type) {
+       case KEY_RSA:
+               if (bits == 0)
+                       return 0;
+               /* Get public exponent, public modulus. */
+               if (!read_bignum(cpp, ret->rsa->e))
+                       return 0;
+               if (!read_bignum(cpp, ret->rsa->n))
+                       return 0;
+               break;
+       case KEY_DSA:
+               if (bits != 0)
+                       return 0;
+               if (!read_bignum(cpp, ret->dsa->p))
+                       return 0;
+               if (!read_bignum(cpp, ret->dsa->q))
+                       return 0;
+               if (!read_bignum(cpp, ret->dsa->g))
+                       return 0;
+               if (!read_bignum(cpp, ret->dsa->pub_key))
+                       return 0;
+               break;
+       default:
+               fatal("bad key type: %d", ret->type);
+               break;
+       }
+       return 1;
+}
+int
+key_write(Key *key, FILE *f)
+{
+       int success = 0;
+       unsigned int bits = 0;
+
+       if (key->type == KEY_RSA && key->rsa != NULL) {
+               /* size of modulus 'n' */
+               bits = BN_num_bits(key->rsa->n);
+               fprintf(f, "%u", bits);
+               if (write_bignum(f, key->rsa->e) &&
+                   write_bignum(f, key->rsa->n)) {
+                       success = 1;
+               } else {
+                       error("key_write: failed for RSA key");
+               }
+       } else if (key->type == KEY_DSA && key->dsa != NULL) {
+               /* bits == 0 means DSA key */
+               bits = 0;
+               fprintf(f, "%u", bits);
+               if (write_bignum(f, key->dsa->p) &&
+                   write_bignum(f, key->dsa->q) &&
+                   write_bignum(f, key->dsa->g) &&
+                   write_bignum(f, key->dsa->pub_key)) {
+                       success = 1;
+               } else {
+                       error("key_write: failed for DSA key");
+               }
+       }
+       return success;
+}
diff --git a/usr.bin/ssh/key.h b/usr.bin/ssh/key.h
new file mode 100644 (file)
index 0000000..70f0c51
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef KEY_H
+#define KEY_H
+
+typedef struct Key Key;
+enum types {
+       KEY_RSA,
+       KEY_DSA,
+       KEY_EMPTY
+};
+struct Key {
+       int     type;
+       RSA     *rsa;
+       DSA     *dsa;
+};
+
+Key    *key_new(int type);
+void   key_free(Key *k);
+int    key_equal(Key *a, Key *b);
+char   *key_fingerprint(Key *k);
+int    key_write(Key *key, FILE *f);
+int    key_read(Key *key, unsigned int bits, char **cpp);
+
+#endif
index 51aedd4..4a9ce1c 100644 (file)
@@ -4,7 +4,8 @@ LIB=    ssh
 SRCS=   authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \
        cipher.c compat.c compress.c crc32.c deattack.c fingerprint.c \
        hostfile.c log.c match.c mpaux.c nchan.c packet.c readpass.c \
-       rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c
+       rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \
+       key.c
 
 NOPROFILE= yes
 NOPIC= yes
index 7a63be6..aadcfd6 100644 (file)
@@ -14,7 +14,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: match.c,v 1.4 1999/11/24 19:53:48 markus Exp $");
+RCSID("$Id: match.c,v 1.5 2000/03/23 22:15:33 markus Exp $");
 
 #include "ssh.h"
 
@@ -80,3 +80,62 @@ match_pattern(const char *s, const char *pattern)
        }
        /* NOTREACHED */
 }
+
+/*
+ * Tries to match the host name (which must be in all lowercase) against the
+ * comma-separated sequence of subpatterns (each possibly preceded by ! to
+ * indicate negation).  Returns true if there is a positive match; zero
+ * otherwise.
+ */
+
+int
+match_hostname(const char *host, const char *pattern, unsigned int len)
+{
+       char sub[1024];
+       int negated;
+       int got_positive;
+       unsigned int i, subi;
+
+       got_positive = 0;
+       for (i = 0; i < len;) {
+               /* Check if the subpattern is negated. */
+               if (pattern[i] == '!') {
+                       negated = 1;
+                       i++;
+               } else
+                       negated = 0;
+
+               /*
+                * Extract the subpattern up to a comma or end.  Convert the
+                * subpattern to lowercase.
+                */
+               for (subi = 0;
+                    i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
+                    subi++, i++)
+                       sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
+               /* If subpattern too long, return failure (no match). */
+               if (subi >= sizeof(sub) - 1)
+                       return 0;
+
+               /* If the subpattern was terminated by a comma, skip the comma. */
+               if (i < len && pattern[i] == ',')
+                       i++;
+
+               /* Null-terminate the subpattern. */
+               sub[subi] = '\0';
+
+               /* Try to match the subpattern against the host name. */
+               if (match_pattern(host, sub)) {
+                       if (negated)
+                               return 0;       /* Fail */
+                       else
+                               got_positive = 1;
+               }
+       }
+
+       /*
+        * Return success if got a positive match.  If there was a negative
+        * match, we have already returned zero and never get here.
+        */
+       return got_positive;
+}
diff --git a/usr.bin/ssh/match.h b/usr.bin/ssh/match.h
new file mode 100644 (file)
index 0000000..4625d97
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef MATCH_H
+#define MATCH_H
+
+/*
+ * Returns true if the given string matches the pattern (which may contain ?
+ * and * as wildcards), and zero if it does not match.
+ */
+int     match_pattern(const char *s, const char *pattern);
+
+/*
+ * Tries to match the host name (which must be in all lowercase) against the
+ * comma-separated sequence of subpatterns (each possibly preceded by ! to
+ * indicate negation).  Returns true if there is a positive match; zero
+ * otherwise.
+ */
+int     match_hostname(const char *host, const char *pattern, unsigned int len);
+
+#endif
index 47872ce..78f95f8 100644 (file)
@@ -13,7 +13,7 @@
  * 
  */
 
-/* RCSID("$Id: ssh.h,v 1.33 2000/02/01 22:32:53 d Exp $"); */
+/* RCSID("$Id: ssh.h,v 1.34 2000/03/23 22:15:33 markus Exp $"); */
 
 #ifndef SSH_H
 #define SSH_H
@@ -313,8 +313,7 @@ int     auth_rhosts(struct passwd * pw, const char *client_user);
  * its host key.  Returns true if authentication succeeds.
  */
 int 
-auth_rhosts_rsa(struct passwd * pw, const char *client_user,
-    BIGNUM * client_host_key_e, BIGNUM * client_host_key_n);
+auth_rhosts_rsa(struct passwd * pw, const char *client_user, RSA* client_host_key);
 
 /*
  * Tries to authenticate the user using password.  Returns true if
@@ -362,41 +361,12 @@ int     get_remote_port(void);
 int    get_local_port(void);
 
 
-/*
- * Tries to match the host name (which must be in all lowercase) against the
- * comma-separated sequence of subpatterns (each possibly preceded by ! to
- * indicate negation).  Returns true if there is a positive match; zero
- * otherwise.
- */
-int     match_hostname(const char *host, const char *pattern, unsigned int len);
-
-/*
- * Checks whether the given host is already in the list of our known hosts.
- * Returns HOST_OK if the host is known and has the specified key, HOST_NEW
- * if the host is not known, and HOST_CHANGED if the host is known but used
- * to have a different host key.  The host must be in all lowercase.
- */
-typedef enum {
-       HOST_OK, HOST_NEW, HOST_CHANGED
-}       HostStatus;
-HostStatus 
-check_host_in_hostfile(const char *filename, const char *host,
-    BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn);
-
-/*
- * Appends an entry to the host file.  Returns false if the entry could not
- * be appended.
- */
-int 
-add_host_to_hostfile(const char *filename, const char *host,
-    BIGNUM * e, BIGNUM * n);
-
 /*
  * Performs the RSA authentication challenge-response dialog with the client,
  * and returns true (non-zero) if the client gave the correct answer to our
  * challenge; returns zero if the client gives a wrong answer.
  */
-int     auth_rsa_challenge_dialog(BIGNUM * e, BIGNUM * n);
+int     auth_rsa_challenge_dialog(RSA *pk);
 
 /*
  * Reads a passphrase from /dev/tty with echo turned off.  Returns the
index 3d273ed..0727798 100644 (file)
@@ -8,7 +8,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect.c,v 1.57 2000/03/16 20:56:14 markus Exp $");
+RCSID("$OpenBSD: sshconnect.c,v 1.58 2000/03/23 22:15:33 markus Exp $");
 
 #include <ssl/bn.h>
 #include "xmalloc.h"
@@ -21,9 +21,12 @@ RCSID("$OpenBSD: sshconnect.c,v 1.57 2000/03/16 20:56:14 markus Exp $");
 #include "uidswap.h"
 #include "compat.h"
 #include "readconf.h"
-#include "fingerprint.h"
 
+#include <ssl/rsa.h>
+#include <ssl/dsa.h>
 #include <ssl/md5.h>
+#include "key.h"
+#include "hostfile.h"
 
 /* Session id for the current session. */
 unsigned char session_id[16];
@@ -1067,9 +1070,9 @@ read_yes_or_no(const char *prompt, int defval)
  */
 
 void
-check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
+check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
 {
-       RSA *file_key;
+       Key *file_key;
        char *ip = NULL;
        char hostline[1000], *hostp;
        HostStatus host_status;
@@ -1119,47 +1122,34 @@ check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
         * Store the host key from the known host file in here so that we can
         * compare it with the key for the IP address.
         */
-       file_key = RSA_new();
-       file_key->n = BN_new();
-       file_key->e = BN_new();
+       file_key = key_new(host_key->type);
 
        /*
         * Check if the host key is present in the user\'s list of known
         * hosts or in the systemwide list.
         */
-       host_status = check_host_in_hostfile(options.user_hostfile, host,
-                                            host_key->e, host_key->n,
-                                            file_key->e, file_key->n);
+       host_status = check_host_in_hostfile(options.user_hostfile, host, host_key, file_key);
        if (host_status == HOST_NEW)
-               host_status = check_host_in_hostfile(options.system_hostfile, host,
-                                               host_key->e, host_key->n,
-                                              file_key->e, file_key->n);
+               host_status = check_host_in_hostfile(options.system_hostfile, host, host_key, file_key);
        /*
         * Also perform check for the ip address, skip the check if we are
         * localhost or the hostname was an ip address to begin with
         */
        if (options.check_host_ip && !local && strcmp(host, ip)) {
-               RSA *ip_key = RSA_new();
-               ip_key->n = BN_new();
-               ip_key->e = BN_new();
-               ip_status = check_host_in_hostfile(options.user_hostfile, ip,
-                                               host_key->e, host_key->n,
-                                                  ip_key->e, ip_key->n);
+               Key *ip_key = key_new(host_key->type);
+               ip_status = check_host_in_hostfile(options.user_hostfile, ip, host_key, ip_key);
 
                if (ip_status == HOST_NEW)
-                       ip_status = check_host_in_hostfile(options.system_hostfile, ip,
-                                               host_key->e, host_key->n,
-                                                  ip_key->e, ip_key->n);
+                       ip_status = check_host_in_hostfile(options.system_hostfile, ip, host_key, ip_key);
                if (host_status == HOST_CHANGED &&
-                   (ip_status != HOST_CHANGED ||
-                    (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n))))
+                   (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key)))
                        host_ip_differ = 1;
 
-               RSA_free(ip_key);
+               key_free(ip_key);
        } else
                ip_status = host_status;
 
-       RSA_free(file_key);
+       key_free(file_key);
 
        switch (host_status) {
        case HOST_OK:
@@ -1167,8 +1157,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
                debug("Host '%.200s' is known and matches the host key.", host);
                if (options.check_host_ip) {
                        if (ip_status == HOST_NEW) {
-                               if (!add_host_to_hostfile(options.user_hostfile, ip,
-                                              host_key->e, host_key->n))
+                               if (!add_host_to_hostfile(options.user_hostfile, ip, host_key))
                                        log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).",
                                            ip, options.user_hostfile);
                                else
@@ -1188,12 +1177,12 @@ check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
                } else if (options.strict_host_key_checking == 2) {
                        /* The default */
                        char prompt[1024];
-                       char *fp = fingerprint(host_key->e, host_key->n);
+                       char *fp = key_fingerprint(host_key);
                        snprintf(prompt, sizeof(prompt),
                            "The authenticity of host '%.200s' can't be established.\n"
-                           "Key fingerprint is %d %s.\n"
+                           "Key fingerprint is %s.\n"
                            "Are you sure you want to continue connecting (yes/no)? ",
-                           host, BN_num_bits(host_key->n), fp);
+                           host, fp);
                        if (!read_yes_or_no(prompt, -1))
                                fatal("Aborted by user!\n");
                }
@@ -1204,8 +1193,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
                        hostp = host;
 
                /* If not in strict mode, add the key automatically to the local known_hosts file. */
-               if (!add_host_to_hostfile(options.user_hostfile, hostp,
-                                         host_key->e, host_key->n))
+               if (!add_host_to_hostfile(options.user_hostfile, hostp, host_key))
                        log("Failed to add the host to the list of known hosts (%.500s).",
                            options.user_hostfile);
                else
@@ -1273,6 +1261,14 @@ check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
        if (options.check_host_ip)
                xfree(ip);
 }
+void
+check_rsa_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
+{
+       Key k;
+       k.type = KEY_RSA;
+       k.rsa = host_key;
+       check_host_key(host, hostaddr, &k);
+}
 
 /*
  * SSH1 key exchange
@@ -1348,7 +1344,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
                               8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
                               SSH_SMSG_PUBLIC_KEY);
 
-       check_host_key(host, hostaddr, host_key);
+       check_rsa_host_key(host, hostaddr, host_key);
 
        client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN;
 
@@ -1607,7 +1603,6 @@ ssh_userauth(int host_key_valid, RSA *own_host_key,
        fatal("Permission denied.");
        /* NOTREACHED */
 }
-
 /*
  * Starts a dialog with the server, and authenticates the current user on the
  * server.  This does not need any extra privileges.  The basic connection
@@ -1638,6 +1633,7 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost,
        ssh_kex(host, hostaddr);
        if (supported_authentications == 0)
                fatal("supported_authentications == 0.");
+
        /* authenticate user */
        ssh_userauth(host_key_valid, own_host_key, original_real_uid, host);
 }
index ac8ea40..6a56253 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.93 2000/03/22 09:55:10 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.94 2000/03/23 22:15:34 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -1264,7 +1264,7 @@ do_authloop(struct passwd * pw)
 {
        int attempt = 0;
        unsigned int bits;
-       BIGNUM *client_host_key_e, *client_host_key_n;
+       RSA *client_host_key;
        BIGNUM *n;
        char *client_user, *password;
        char user[1024];
@@ -1381,21 +1381,24 @@ do_authloop(struct passwd * pw)
                        client_user = packet_get_string(&ulen);
 
                        /* Get the client host key. */
-                       client_host_key_e = BN_new();
-                       client_host_key_n = BN_new();
+                       client_host_key = RSA_new();
+                       if (client_host_key == NULL)
+                               fatal("RSA_new failed");
+                       client_host_key->e = BN_new();
+                       client_host_key->n = BN_new();
+                       if (client_host_key->e == NULL || client_host_key->n == NULL)
+                               fatal("BN_new failed");
                        bits = packet_get_int();
-                       packet_get_bignum(client_host_key_e, &elen);
-                       packet_get_bignum(client_host_key_n, &nlen);
+                       packet_get_bignum(client_host_key->e, &elen);
+                       packet_get_bignum(client_host_key->n, &nlen);
 
-                       if (bits != BN_num_bits(client_host_key_n))
+                       if (bits != BN_num_bits(client_host_key->n))
                                error("Warning: keysize mismatch for client_host_key: "
-                                     "actual %d, announced %d", BN_num_bits(client_host_key_n), bits);
+                                     "actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
                        packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
 
-                       authenticated = auth_rhosts_rsa(pw, client_user,
-                                  client_host_key_e, client_host_key_n);
-                       BN_clear_free(client_host_key_e);
-                       BN_clear_free(client_host_key_n);
+                       authenticated = auth_rhosts_rsa(pw, client_user, client_host_key);
+                       RSA_free(client_host_key);
 
                        snprintf(user, sizeof user, " ruser %s", client_user);
                        xfree(client_user);