From be14628f3ce880cece503400a53bb33560d1a536 Mon Sep 17 00:00:00 2001 From: Markus Friedl Date: Thu, 23 Mar 2000 22:15:35 +0000 Subject: initial support for DSA keys. ok deraadt@, niels@ --- usr.bin/ssh/auth-rh-rsa.c | 40 ++++--- usr.bin/ssh/auth-rsa.c | 37 +++--- usr.bin/ssh/hostfile.c | 193 ++++++++---------------------- usr.bin/ssh/hostfile.h | 22 ++++ usr.bin/ssh/key.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++ usr.bin/ssh/key.h | 23 ++++ usr.bin/ssh/lib/Makefile | 3 +- usr.bin/ssh/match.c | 61 +++++++++- usr.bin/ssh/match.h | 18 +++ usr.bin/ssh/ssh.h | 36 +----- usr.bin/ssh/sshconnect.c | 66 +++++------ usr.bin/ssh/sshd.c | 27 +++-- 12 files changed, 552 insertions(+), 264 deletions(-) create mode 100644 usr.bin/ssh/hostfile.h create mode 100644 usr.bin/ssh/key.c create mode 100644 usr.bin/ssh/key.h create mode 100644 usr.bin/ssh/match.h (limited to 'usr.bin') diff --git a/usr.bin/ssh/auth-rh-rsa.c b/usr.bin/ssh/auth-rh-rsa.c index a9195f0d96b..b7adab7b96b 100644 --- a/usr.bin/ssh/auth-rh-rsa.c +++ b/usr.bin/ssh/auth-rh-rsa.c @@ -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 +#include +#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; } diff --git a/usr.bin/ssh/auth-rsa.c b/usr.bin/ssh/auth-rsa.c index a04adf67c95..3d2e84f1c06 100644 --- a/usr.bin/ssh/auth-rsa.c +++ b/usr.bin/ssh/auth-rsa.c @@ -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 @@ -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."); diff --git a/usr.bin/ssh/hostfile.c b/usr.bin/ssh/hostfile.c index ea92fa04855..eca68da730d 100644 --- a/usr.bin/ssh/hostfile.c +++ b/usr.bin/ssh/hostfile.c @@ -14,63 +14,23 @@ */ #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 +#include +#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 index 00000000000..64fe185da9f --- /dev/null +++ b/usr.bin/ssh/hostfile.h @@ -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 index 00000000000..6ad35cbac9d --- /dev/null +++ b/usr.bin/ssh/key.c @@ -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 , Espoo, Finland + */ + +#include "includes.h" +#include "ssh.h" +#include +#include +#include +#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 + */ +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 index 00000000000..70f0c518b8a --- /dev/null +++ b/usr.bin/ssh/key.h @@ -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 diff --git a/usr.bin/ssh/lib/Makefile b/usr.bin/ssh/lib/Makefile index 51aedd4a3e3..4a9ce1cb0ae 100644 --- a/usr.bin/ssh/lib/Makefile +++ b/usr.bin/ssh/lib/Makefile @@ -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 diff --git a/usr.bin/ssh/match.c b/usr.bin/ssh/match.c index 7a63be63fb9..aadcfd6e940 100644 --- a/usr.bin/ssh/match.c +++ b/usr.bin/ssh/match.c @@ -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 index 00000000000..4625d97691f --- /dev/null +++ b/usr.bin/ssh/match.h @@ -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 diff --git a/usr.bin/ssh/ssh.h b/usr.bin/ssh/ssh.h index 47872ce8986..78f95f89d85 100644 --- a/usr.bin/ssh/ssh.h +++ b/usr.bin/ssh/ssh.h @@ -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 diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index 3d273edaac0..07277986949 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -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 #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 +#include #include +#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); } diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index ac8ea406b96..6a5625394ff 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -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); -- cgit v1.2.3