[BACK]Return to hostfile.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

File: [local] / src / usr.bin / ssh / hostfile.c (download)

Revision 1.1, Sun Sep 26 20:53:36 1999 UTC (24 years, 8 months ago) by deraadt
Branch: MAIN

i bet a lot of people didn't know what ssh 1.2.16 had a nice license.
well, except for the patent issues.  someone in sweden (forget their
name at the moment) cleaned out most of the patented code, and now
this code removes rsa code.  when this is done, it will link against
libssl, but the work isn't completely done yet.  then we need to bring
this up to modern days, featurewise.

/*

hostfile.c

Author: Tatu Ylonen <ylo@cs.hut.fi>

Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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("$Id: hostfile.c,v 1.1 1999/09/26 20:53:36 deraadt 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_mp_int(char **cpp, MP_INT *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 (mpz_set_str(value, *cpp, 10) != 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, MP_INT *e, MP_INT *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_mp_int(&cp, e))
    return 0;

  /* Get public modulus. */
  if (!auth_rsa_read_mp_int(&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 if host matches any negated subpattern. */
        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, unsigned int bits,
				  MP_INT *e, MP_INT *n)
{
  FILE *f;
  char line[8192];
  MP_INT ke, kn;
  unsigned int kbits, hostlen;
  char *cp, *cp2;
  HostStatus end_return;
  struct stat st;

  /* Open the file containing the list of known hosts. */
  f = fopen(filename, "r");
  if (!f)
    {
      if (stat(filename, &st) >= 0)
	{
	  packet_send_debug("Could not open %.900s for reading.", filename);
	  packet_send_debug("If your home directory is on an NFS volume, it may need to be world-readable.");
	}
      return HOST_NEW;
    }

  /* Initialize mp-int variables. */
  mpz_init(&ke);
  mpz_init(&kn);
  
  /* 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;

  /* Go trough the file. */
  while (fgets(line, sizeof(line), f))
    {
      cp = line;

      /* Skip any leading whitespace. */
      for (; *cp == ' ' || *cp == '\t'; cp++)
	;

      /* Ignore comment lines and empty lines. */
      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;

      /* Check if the current key is the same as the previous one. */
      if (kbits == bits && mpz_cmp(&ke, e) == 0 && mpz_cmp(&kn, n) == 0)
	{
	  /* Ok, they match. */
	  mpz_clear(&ke);
	  mpz_clear(&kn);
	  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. */
  mpz_clear(&ke);
  mpz_clear(&kn);
  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,
			 unsigned int bits, MP_INT *e, MP_INT *n)
{
  FILE *f;
 
  /* Open the file for appending. */
  f = fopen(filename, "a");
  if (!f)
    return 0;

  /* Print the host name and key to the file. */
  fprintf(f, "%s %u ", host, bits);
  mpz_out_str(f, 10, e);
  fprintf(f, " ");
  mpz_out_str(f, 10, n);
  fprintf(f, "\n");

  /* Close the file. */
  fclose(f);
  return 1;
}