/* * * hostfile.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Thu Jun 29 07:10:56 1995 ylo * * Functions for manipulating the known hosts files. * */ #include "includes.h" RCSID("$OpenBSD: hostfile.c,v 1.10 1999/12/02 20:18:59 markus Exp $"); #include "packet.h" #include "ssh.h" /* * Reads a multiple-precision integer in hex 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 auth_rsa_read_bignum(char **cpp, BIGNUM * value) { char *cp = *cpp; int len, old; /* Skip any leading whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; /* Check that it begins with a hex digit. */ if (*cp < '0' || *cp > '9') return 0; /* Save starting position. */ *cpp = cp; /* Move forward until all hex digits skipped. */ for (; *cp >= '0' && *cp <= '9'; cp++) ; /* Compute the length of the hex number. */ len = cp - *cpp; /* 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; char *cp; /* Skip leading whitespace. */ for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; /* Get number of bits. */ if (*cp < '0' || *cp > '9') return 0; /* Bad bit count... */ 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)) return 0; /* Skip trailing whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; /* Return results. */ *cpp = cp; *bitsp = bits; 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) { 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; } /* * Checks whether the given host (which must be in all lowercase) 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. */ HostStatus check_host_in_hostfile(const char *filename, const char *host, BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn) { FILE *f; char line[8192]; int linenum = 0; unsigned int bits, kbits, hostlen; char *cp, *cp2; HostStatus end_return; /* Open the file containing the list of known hosts. */ f = fopen(filename, "r"); if (!f) return HOST_NEW; /* Cache the length of the host name. */ hostlen = strlen(host); /* * Return value when the loop terminates. This is set to * HOST_CHANGED if we have seen a different key for the host and have * not found the proper one. */ end_return = HOST_NEW; /* size of modulus 'n' */ bits = BN_num_bits(n); /* Go trough the file. */ while (fgets(line, sizeof(line), f)) { cp = line; linenum++; /* Skip any leading whitespace, comments and empty lines. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; /* Check if the host name matches. */ if (!match_hostname(host, cp, (unsigned int) (cp2 - cp))) continue; /* Got a match. Skip host name. */ cp = cp2; /* * 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)) 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) { /* Ok, they match. */ fclose(f); return HOST_OK; } /* * They do not match. We will continue to go through the * file; however, we note that we will not return that it is * new. */ end_return = HOST_CHANGED; } /* Clear variables and close the file. */ fclose(f); /* * Return either HOST_NEW or HOST_CHANGED, depending on whether we * saw a different key for the host. */ return end_return; } /* * 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) { FILE *f; char *buf; unsigned int bits; /* 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 ", 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; }