source: trunk/third/ssh/hostfile.c @ 12646

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