version 1.13, 2000/02/18 10:20:20 |
version 1.14, 2000/03/23 22:15:33 |
|
|
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
#include "packet.h" |
#include "packet.h" |
|
#include "match.h" |
#include "ssh.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 |
* Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the |
* the pointer. The integer must already be initialized. This function is |
* pointer over the key. Skips any whitespace at the beginning and at end. |
* 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 |
int |
auth_rsa_read_bignum(char **cpp, BIGNUM * value) |
hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) |
{ |
{ |
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) |
|
{ |
|
unsigned int bits; |
unsigned int bits; |
char *cp; |
char *cp; |
|
|
|
|
for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) |
for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) |
bits = 10 * bits + *cp - '0'; |
bits = 10 * bits + *cp - '0'; |
|
|
/* Get public exponent. */ |
if (!key_read(ret, bits, &cp)) |
if (!auth_rsa_read_bignum(&cp, e)) |
|
return 0; |
return 0; |
|
|
/* Get public modulus. */ |
|
if (!auth_rsa_read_bignum(&cp, n)) |
|
return 0; |
|
|
|
/* Skip trailing whitespace. */ |
/* Skip trailing whitespace. */ |
for (; *cp == ' ' || *cp == '\t'; cp++) |
for (; *cp == ' ' || *cp == '\t'; cp++) |
; |
; |
|
|
return 1; |
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 |
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]; |
Key *k = key_new(KEY_RSA); |
int negated; |
int ret = hostfile_read_key(cpp, bitsp, k); |
int got_positive; |
BN_copy(e, k->rsa->e); |
unsigned int i, subi; |
BN_copy(n, k->rsa->n); |
|
key_free(k); |
|
return ret; |
|
} |
|
|
got_positive = 0; |
int |
for (i = 0; i < len;) { |
hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum) |
/* Check if the subpattern is negated. */ |
{ |
if (pattern[i] == '!') { |
if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) |
negated = 1; |
return 1; |
i++; |
if (bits != BN_num_bits(key->rsa->n)) { |
} else |
error("Warning: %s, line %d: keysize mismatch for host %s: " |
negated = 0; |
"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.", |
* Extract the subpattern up to a comma or end. Convert the |
bits, BN_num_bits(key->rsa->n), filename, linenum); |
* 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 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; |
|
} |
} |
|
|
/* |
/* |
|
|
*/ |
*/ |
|
|
HostStatus |
HostStatus |
check_host_in_hostfile(const char *filename, const char *host, |
check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found) |
BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn) |
|
{ |
{ |
FILE *f; |
FILE *f; |
char line[8192]; |
char line[8192]; |
|
|
char *cp, *cp2; |
char *cp, *cp2; |
HostStatus end_return; |
HostStatus end_return; |
|
|
|
if (key == NULL) |
|
fatal("no key to look up"); |
/* Open the file containing the list of known hosts. */ |
/* Open the file containing the list of known hosts. */ |
f = fopen(filename, "r"); |
f = fopen(filename, "r"); |
if (!f) |
if (!f) |
|
|
* Extract the key from the line. This will skip any leading |
* Extract the key from the line. This will skip any leading |
* whitespace. Ignore badly formatted lines. |
* whitespace. Ignore badly formatted lines. |
*/ |
*/ |
if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) |
if (!hostfile_read_key(&cp, &kbits, found)) |
continue; |
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. */ |
/* 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. */ |
/* Ok, they match. */ |
fclose(f); |
fclose(f); |
return HOST_OK; |
return HOST_OK; |
|
|
*/ |
*/ |
|
|
int |
int |
add_host_to_hostfile(const char *filename, const char *host, |
add_host_to_hostfile(const char *filename, const char *host, Key *key) |
BIGNUM * e, BIGNUM * n) |
|
{ |
{ |
FILE *f; |
FILE *f; |
char *buf; |
int success = 0; |
unsigned int bits; |
|
|
|
|
if (key == NULL) |
|
return 1; |
|
|
/* Open the file for appending. */ |
/* Open the file for appending. */ |
f = fopen(filename, "a"); |
f = fopen(filename, "a"); |
if (!f) |
if (!f) |
return 0; |
return 0; |
|
|
/* size of modulus 'n' */ |
fprintf(f, "%s ", host); |
bits = BN_num_bits(n); |
if (key_write(key, f)) { |
|
fprintf(f, "\n"); |
/* Print the host name and key to the file. */ |
success = 1; |
fprintf(f, "%s %u ", host, bits); |
} else { |
buf = BN_bn2dec(e); |
error("add_host_to_hostfile: saving key failed"); |
if (buf == NULL) { |
|
error("add_host_to_hostfile: BN_bn2dec(e) failed"); |
|
fclose(f); |
|
return 0; |
|
} |
} |
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. */ |
/* Close the file. */ |
fclose(f); |
fclose(f); |
return 1; |
return success; |
} |
} |