Annotation of src/usr.bin/ssh/hostfile.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2:
! 3: hostfile.c
! 4:
! 5: Author: Tatu Ylonen <ylo@cs.hut.fi>
! 6:
! 7: Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
! 8: All rights reserved
! 9:
! 10: Created: Thu Jun 29 07:10:56 1995 ylo
! 11:
! 12: Functions for manipulating the known hosts files.
! 13:
! 14: */
! 15:
! 16: #include "includes.h"
! 17: RCSID("$Id: hostfile.c,v 1.2 1999/05/04 11:58:44 bg Exp $");
! 18:
! 19: #include "packet.h"
! 20: #include "ssh.h"
! 21:
! 22: /* Reads a multiple-precision integer in hex from the buffer, and advances the
! 23: pointer. The integer must already be initialized. This function is
! 24: permitted to modify the buffer. This leaves *cpp to point just beyond
! 25: the last processed (and maybe modified) character. Note that this may
! 26: modify the buffer containing the number. */
! 27:
! 28: int auth_rsa_read_mp_int(char **cpp, MP_INT *value)
! 29: {
! 30: char *cp = *cpp;
! 31: int len, old;
! 32:
! 33: /* Skip any leading whitespace. */
! 34: for (; *cp == ' ' || *cp == '\t'; cp++)
! 35: ;
! 36:
! 37: /* Check that it begins with a hex digit. */
! 38: if (*cp < '0' || *cp > '9')
! 39: return 0;
! 40:
! 41: /* Save starting position. */
! 42: *cpp = cp;
! 43:
! 44: /* Move forward until all hex digits skipped. */
! 45: for (; *cp >= '0' && *cp <= '9'; cp++)
! 46: ;
! 47:
! 48: /* Compute the length of the hex number. */
! 49: len = cp - *cpp;
! 50:
! 51: /* Save the old terminating character, and replace it by \0. */
! 52: old = *cp;
! 53: *cp = 0;
! 54:
! 55: /* Parse the number. */
! 56: if (mpz_set_str(value, *cpp, 10) != 0)
! 57: return 0;
! 58:
! 59: /* Restore old terminating character. */
! 60: *cp = old;
! 61:
! 62: /* Move beyond the number and return success. */
! 63: *cpp = cp;
! 64: return 1;
! 65: }
! 66:
! 67: /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer
! 68: over the key. Skips any whitespace at the beginning and at end. */
! 69:
! 70: int auth_rsa_read_key(char **cpp, unsigned int *bitsp, MP_INT *e, MP_INT *n)
! 71: {
! 72: unsigned int bits;
! 73: char *cp;
! 74:
! 75: /* Skip leading whitespace. */
! 76: for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
! 77: ;
! 78:
! 79: /* Get number of bits. */
! 80: if (*cp < '0' || *cp > '9')
! 81: return 0; /* Bad bit count... */
! 82: for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
! 83: bits = 10 * bits + *cp - '0';
! 84:
! 85: /* Get public exponent. */
! 86: if (!auth_rsa_read_mp_int(&cp, e))
! 87: return 0;
! 88:
! 89: /* Get public modulus. */
! 90: if (!auth_rsa_read_mp_int(&cp, n))
! 91: return 0;
! 92:
! 93: /* Skip trailing whitespace. */
! 94: for (; *cp == ' ' || *cp == '\t'; cp++)
! 95: ;
! 96:
! 97: /* Return results. */
! 98: *cpp = cp;
! 99: *bitsp = bits;
! 100: return 1;
! 101: }
! 102:
! 103: /* Tries to match the host name (which must be in all lowercase) against the
! 104: comma-separated sequence of subpatterns (each possibly preceded by ! to
! 105: indicate negation). Returns true if there is a positive match; zero
! 106: otherwise. */
! 107:
! 108: int match_hostname(const char *host, const char *pattern, unsigned int len)
! 109: {
! 110: char sub[1024];
! 111: int negated;
! 112: int got_positive;
! 113: unsigned int i, subi;
! 114:
! 115: got_positive = 0;
! 116: for (i = 0; i < len;)
! 117: {
! 118: /* Check if the subpattern is negated. */
! 119: if (pattern[i] == '!')
! 120: {
! 121: negated = 1;
! 122: i++;
! 123: }
! 124: else
! 125: negated = 0;
! 126:
! 127: /* Extract the subpattern up to a comma or end. Convert the subpattern
! 128: to lowercase. */
! 129: for (subi = 0;
! 130: i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
! 131: subi++, i++)
! 132: sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
! 133: /* If subpattern too long, return failure (no match). */
! 134: if (subi >= sizeof(sub) - 1)
! 135: return 0;
! 136:
! 137: /* If the subpattern was terminated by a comma, skip the comma. */
! 138: if (i < len && pattern[i] == ',')
! 139: i++;
! 140:
! 141: /* Null-terminate the subpattern. */
! 142: sub[subi] = '\0';
! 143:
! 144: /* Try to match the subpattern against the host name. */
! 145: if (match_pattern(host, sub))
! 146: if (negated)
! 147: return 0; /* Fail if host matches any negated subpattern. */
! 148: else
! 149: got_positive = 1;
! 150: }
! 151:
! 152: /* Return success if got a positive match. If there was a negative match,
! 153: we have already returned zero and never get here. */
! 154: return got_positive;
! 155: }
! 156:
! 157: /* Checks whether the given host (which must be in all lowercase) is
! 158: already in the list of our known hosts.
! 159: Returns HOST_OK if the host is known and has the specified key,
! 160: HOST_NEW if the host is not known, and HOST_CHANGED if the host is known
! 161: but used to have a different host key. */
! 162:
! 163: HostStatus check_host_in_hostfile(const char *filename,
! 164: const char *host, unsigned int bits,
! 165: MP_INT *e, MP_INT *n)
! 166: {
! 167: FILE *f;
! 168: char line[8192];
! 169: MP_INT ke, kn;
! 170: unsigned int kbits, hostlen;
! 171: char *cp, *cp2;
! 172: HostStatus end_return;
! 173: struct stat st;
! 174:
! 175: /* Open the file containing the list of known hosts. */
! 176: f = fopen(filename, "r");
! 177: if (!f)
! 178: {
! 179: if (stat(filename, &st) >= 0)
! 180: {
! 181: packet_send_debug("Could not open %.900s for reading.", filename);
! 182: packet_send_debug("If your home directory is on an NFS volume, it may need to be world-readable.");
! 183: }
! 184: return HOST_NEW;
! 185: }
! 186:
! 187: /* Initialize mp-int variables. */
! 188: mpz_init(&ke);
! 189: mpz_init(&kn);
! 190:
! 191: /* Cache the length of the host name. */
! 192: hostlen = strlen(host);
! 193:
! 194: /* Return value when the loop terminates. This is set to HOST_CHANGED if
! 195: we have seen a different key for the host and have not found the proper
! 196: one. */
! 197: end_return = HOST_NEW;
! 198:
! 199: /* Go trough the file. */
! 200: while (fgets(line, sizeof(line), f))
! 201: {
! 202: cp = line;
! 203:
! 204: /* Skip any leading whitespace. */
! 205: for (; *cp == ' ' || *cp == '\t'; cp++)
! 206: ;
! 207:
! 208: /* Ignore comment lines and empty lines. */
! 209: if (!*cp || *cp == '#' || *cp == '\n')
! 210: continue;
! 211:
! 212: /* Find the end of the host name portion. */
! 213: for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
! 214: ;
! 215:
! 216: /* Check if the host name matches. */
! 217: if (!match_hostname(host, cp, (unsigned int)(cp2 - cp)))
! 218: continue;
! 219:
! 220: /* Got a match. Skip host name. */
! 221: cp = cp2;
! 222:
! 223: /* Extract the key from the line. This will skip any leading
! 224: whitespace. Ignore badly formatted lines. */
! 225: if (!auth_rsa_read_key(&cp, &kbits, &ke, &kn))
! 226: continue;
! 227:
! 228: /* Check if the current key is the same as the previous one. */
! 229: if (kbits == bits && mpz_cmp(&ke, e) == 0 && mpz_cmp(&kn, n) == 0)
! 230: {
! 231: /* Ok, they match. */
! 232: mpz_clear(&ke);
! 233: mpz_clear(&kn);
! 234: fclose(f);
! 235: return HOST_OK;
! 236: }
! 237:
! 238: /* They do not match. We will continue to go through the file; however,
! 239: we note that we will not return that it is new. */
! 240: end_return = HOST_CHANGED;
! 241: }
! 242: /* Clear variables and close the file. */
! 243: mpz_clear(&ke);
! 244: mpz_clear(&kn);
! 245: fclose(f);
! 246:
! 247: /* Return either HOST_NEW or HOST_CHANGED, depending on whether we saw a
! 248: different key for the host. */
! 249: return end_return;
! 250: }
! 251:
! 252: /* Appends an entry to the host file. Returns false if the entry
! 253: could not be appended. */
! 254:
! 255: int add_host_to_hostfile(const char *filename, const char *host,
! 256: unsigned int bits, MP_INT *e, MP_INT *n)
! 257: {
! 258: FILE *f;
! 259:
! 260: /* Open the file for appending. */
! 261: f = fopen(filename, "a");
! 262: if (!f)
! 263: return 0;
! 264:
! 265: /* Print the host name and key to the file. */
! 266: fprintf(f, "%s %u ", host, bits);
! 267: mpz_out_str(f, 10, e);
! 268: fprintf(f, " ");
! 269: mpz_out_str(f, 10, n);
! 270: fprintf(f, "\n");
! 271:
! 272: /* Close the file. */
! 273: fclose(f);
! 274: return 1;
! 275: }