1 /* $OpenBSD: hostfile.c,v 1.66 2015/05/04 06:10:48 djm Exp $ */
3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6 * Functions for manipulating the known hosts files.
8 * As far as I am concerned, the code I have written for this software
9 * can be used freely for any purpose. Any derived versions of this
10 * software must be clearly marked as such, and if the derived work is
11 * incompatible with the protocol description in the RFC file, it must be
12 * called by a name other than "ssh" or "Secure Shell".
15 * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved.
16 * Copyright (c) 1999 Niels Provos. All rights reserved.
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 #include <sys/types.h>
44 #include <netinet/in.h>
66 struct hostkey_entry
*entries
;
70 /* XXX hmac is too easy to dictionary attack; use bcrypt? */
73 extract_salt(const char *s
, u_int l
, u_char
*salt
, size_t salt_len
)
79 if (l
< sizeof(HASH_MAGIC
) - 1) {
80 debug2("extract_salt: string too short");
83 if (strncmp(s
, HASH_MAGIC
, sizeof(HASH_MAGIC
) - 1) != 0) {
84 debug2("extract_salt: invalid magic identifier");
87 s
+= sizeof(HASH_MAGIC
) - 1;
88 l
-= sizeof(HASH_MAGIC
) - 1;
89 if ((p
= memchr(s
, HASH_DELIM
, l
)) == NULL
) {
90 debug2("extract_salt: missing salt termination character");
96 if (b64len
== 0 || b64len
> 1024) {
97 debug2("extract_salt: bad encoded salt length %u", b64len
);
100 b64salt
= xmalloc(1 + b64len
);
101 memcpy(b64salt
, s
, b64len
);
102 b64salt
[b64len
] = '\0';
104 ret
= __b64_pton(b64salt
, salt
, salt_len
);
107 debug2("extract_salt: salt decode error");
110 if (ret
!= (int)ssh_hmac_bytes(SSH_DIGEST_SHA1
)) {
111 debug2("extract_salt: expected salt len %zd, got %d",
112 ssh_hmac_bytes(SSH_DIGEST_SHA1
), ret
);
120 host_hash(const char *host
, const char *name_from_hostfile
, u_int src_len
)
122 struct ssh_hmac_ctx
*ctx
;
123 u_char salt
[256], result
[256];
124 char uu_salt
[512], uu_result
[512];
125 static char encoded
[1024];
128 len
= ssh_digest_bytes(SSH_DIGEST_SHA1
);
130 if (name_from_hostfile
== NULL
) {
131 /* Create new salt */
132 for (i
= 0; i
< len
; i
++)
133 salt
[i
] = arc4random();
135 /* Extract salt from known host entry */
136 if (extract_salt(name_from_hostfile
, src_len
, salt
,
141 if ((ctx
= ssh_hmac_start(SSH_DIGEST_SHA1
)) == NULL
||
142 ssh_hmac_init(ctx
, salt
, len
) < 0 ||
143 ssh_hmac_update(ctx
, host
, strlen(host
)) < 0 ||
144 ssh_hmac_final(ctx
, result
, sizeof(result
)))
145 fatal("%s: ssh_hmac failed", __func__
);
148 if (__b64_ntop(salt
, len
, uu_salt
, sizeof(uu_salt
)) == -1 ||
149 __b64_ntop(result
, len
, uu_result
, sizeof(uu_result
)) == -1)
150 fatal("%s: __b64_ntop failed", __func__
);
152 snprintf(encoded
, sizeof(encoded
), "%s%s%c%s", HASH_MAGIC
, uu_salt
,
153 HASH_DELIM
, uu_result
);
159 * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the
160 * pointer over the key. Skips any whitespace at the beginning and at end.
164 hostfile_read_key(char **cpp
, u_int
*bitsp
, struct sshkey
*ret
)
169 /* Skip leading whitespace. */
170 for (cp
= *cpp
; *cp
== ' ' || *cp
== '\t'; cp
++)
173 if ((r
= sshkey_read(ret
, &cp
)) != 0)
176 /* Skip trailing whitespace. */
177 for (; *cp
== ' ' || *cp
== '\t'; cp
++)
180 /* Return results. */
183 *bitsp
= sshkey_size(ret
);
188 check_markers(char **cpp
)
190 char marker
[32], *sp
, *cp
= *cpp
;
194 /* Only one marker is allowed */
197 /* Markers are terminated by whitespace */
198 if ((sp
= strchr(cp
, ' ')) == NULL
&&
199 (sp
= strchr(cp
, '\t')) == NULL
)
201 /* Extract marker for comparison */
202 if (sp
<= cp
+ 1 || sp
>= cp
+ sizeof(marker
))
204 memcpy(marker
, cp
, sp
- cp
);
205 marker
[sp
- cp
] = '\0';
206 if (strcmp(marker
, CA_MARKER
) == 0)
208 else if (strcmp(marker
, REVOKE_MARKER
) == 0)
213 /* Skip past marker and any whitespace that follows it */
215 for (; *cp
== ' ' || *cp
== '\t'; cp
++)
225 struct hostkeys
*ret
= xcalloc(1, sizeof(*ret
));
231 struct load_callback_ctx
{
234 struct hostkeys
*hostkeys
;
238 record_hostkey(struct hostkey_foreach_line
*l
, void *_ctx
)
240 struct load_callback_ctx
*ctx
= (struct load_callback_ctx
*)_ctx
;
241 struct hostkeys
*hostkeys
= ctx
->hostkeys
;
242 struct hostkey_entry
*tmp
;
244 if (l
->status
== HKF_STATUS_INVALID
) {
245 /* XXX make this verbose() in the future */
246 debug("%s:%ld: parse error in hostkeys file",
247 l
->path
, l
->linenum
);
251 debug3("%s: found %skey type %s in file %s:%lu", __func__
,
252 l
->marker
== MRK_NONE
? "" :
253 (l
->marker
== MRK_CA
? "ca " : "revoked "),
254 sshkey_type(l
->key
), l
->path
, l
->linenum
);
255 if ((tmp
= reallocarray(hostkeys
->entries
,
256 hostkeys
->num_entries
+ 1, sizeof(*hostkeys
->entries
))) == NULL
)
257 return SSH_ERR_ALLOC_FAIL
;
258 hostkeys
->entries
= tmp
;
259 hostkeys
->entries
[hostkeys
->num_entries
].host
= xstrdup(ctx
->host
);
260 hostkeys
->entries
[hostkeys
->num_entries
].file
= xstrdup(l
->path
);
261 hostkeys
->entries
[hostkeys
->num_entries
].line
= l
->linenum
;
262 hostkeys
->entries
[hostkeys
->num_entries
].key
= l
->key
;
263 l
->key
= NULL
; /* steal it */
264 hostkeys
->entries
[hostkeys
->num_entries
].marker
= l
->marker
;
265 hostkeys
->num_entries
++;
272 load_hostkeys(struct hostkeys
*hostkeys
, const char *host
, const char *path
)
275 struct load_callback_ctx ctx
;
279 ctx
.hostkeys
= hostkeys
;
281 if ((r
= hostkeys_foreach(path
, record_hostkey
, &ctx
, host
, NULL
,
282 HKF_WANT_MATCH
|HKF_WANT_PARSE_KEY
)) != 0) {
283 if (r
!= SSH_ERR_SYSTEM_ERROR
&& errno
!= ENOENT
)
284 debug("%s: hostkeys_foreach failed for %s: %s",
285 __func__
, path
, ssh_err(r
));
287 if (ctx
.num_loaded
!= 0)
288 debug3("%s: loaded %lu keys from %s", __func__
,
289 ctx
.num_loaded
, host
);
293 free_hostkeys(struct hostkeys
*hostkeys
)
297 for (i
= 0; i
< hostkeys
->num_entries
; i
++) {
298 free(hostkeys
->entries
[i
].host
);
299 free(hostkeys
->entries
[i
].file
);
300 sshkey_free(hostkeys
->entries
[i
].key
);
301 explicit_bzero(hostkeys
->entries
+ i
, sizeof(*hostkeys
->entries
));
303 free(hostkeys
->entries
);
304 explicit_bzero(hostkeys
, sizeof(*hostkeys
));
309 check_key_not_revoked(struct hostkeys
*hostkeys
, struct sshkey
*k
)
311 int is_cert
= sshkey_is_cert(k
);
314 for (i
= 0; i
< hostkeys
->num_entries
; i
++) {
315 if (hostkeys
->entries
[i
].marker
!= MRK_REVOKE
)
317 if (sshkey_equal_public(k
, hostkeys
->entries
[i
].key
))
320 sshkey_equal_public(k
->cert
->signature_key
,
321 hostkeys
->entries
[i
].key
))
328 * Match keys against a specified key, or look one up by key type.
330 * If looking for a keytype (key == NULL) and one is found then return
331 * HOST_FOUND, otherwise HOST_NEW.
333 * If looking for a key (key != NULL):
334 * 1. If the key is a cert and a matching CA is found, return HOST_OK
335 * 2. If the key is not a cert and a matching key is found, return HOST_OK
336 * 3. If no key matches but a key with a different type is found, then
337 * return HOST_CHANGED
338 * 4. If no matching keys are found, then return HOST_NEW.
340 * Finally, check any found key is not revoked.
343 check_hostkeys_by_key_or_type(struct hostkeys
*hostkeys
,
344 struct sshkey
*k
, int keytype
, const struct hostkey_entry
**found
)
347 HostStatus end_return
= HOST_NEW
;
348 int want_cert
= sshkey_is_cert(k
);
349 HostkeyMarker want_marker
= want_cert
? MRK_CA
: MRK_NONE
;
350 int proto
= (k
? k
->type
: keytype
) == KEY_RSA1
? 1 : 2;
355 for (i
= 0; i
< hostkeys
->num_entries
; i
++) {
356 if (proto
== 1 && hostkeys
->entries
[i
].key
->type
!= KEY_RSA1
)
358 if (proto
== 2 && hostkeys
->entries
[i
].key
->type
== KEY_RSA1
)
360 if (hostkeys
->entries
[i
].marker
!= want_marker
)
363 if (hostkeys
->entries
[i
].key
->type
!= keytype
)
365 end_return
= HOST_FOUND
;
367 *found
= hostkeys
->entries
+ i
;
368 k
= hostkeys
->entries
[i
].key
;
372 if (sshkey_equal_public(k
->cert
->signature_key
,
373 hostkeys
->entries
[i
].key
)) {
374 /* A matching CA exists */
375 end_return
= HOST_OK
;
377 *found
= hostkeys
->entries
+ i
;
381 if (sshkey_equal(k
, hostkeys
->entries
[i
].key
)) {
382 end_return
= HOST_OK
;
384 *found
= hostkeys
->entries
+ i
;
387 /* A non-maching key exists */
388 end_return
= HOST_CHANGED
;
390 *found
= hostkeys
->entries
+ i
;
393 if (check_key_not_revoked(hostkeys
, k
) != 0) {
394 end_return
= HOST_REVOKED
;
402 check_key_in_hostkeys(struct hostkeys
*hostkeys
, struct sshkey
*key
,
403 const struct hostkey_entry
**found
)
406 fatal("no key to look up");
407 return check_hostkeys_by_key_or_type(hostkeys
, key
, 0, found
);
411 lookup_key_in_hostkeys_by_type(struct hostkeys
*hostkeys
, int keytype
,
412 const struct hostkey_entry
**found
)
414 return (check_hostkeys_by_key_or_type(hostkeys
, NULL
, keytype
,
415 found
) == HOST_FOUND
);
419 write_host_entry(FILE *f
, const char *host
, const char *ip
,
420 const struct sshkey
*key
, int store_hash
)
423 char *hashed_host
= NULL
;
426 if ((hashed_host
= host_hash(host
, NULL
, 0)) == NULL
) {
427 error("%s: host_hash failed", __func__
);
430 fprintf(f
, "%s ", hashed_host
);
431 } else if (ip
!= NULL
)
432 fprintf(f
, "%s,%s ", host
, ip
);
434 fprintf(f
, "%s ", host
);
436 if ((r
= sshkey_write(key
, f
)) == 0)
439 error("%s: sshkey_write failed: %s", __func__
, ssh_err(r
));
445 * Appends an entry to the host file. Returns false if the entry could not
449 add_host_to_hostfile(const char *filename
, const char *host
,
450 const struct sshkey
*key
, int store_hash
)
456 return 1; /* XXX ? */
457 f
= fopen(filename
, "a");
460 success
= write_host_entry(f
, host
, NULL
, key
, store_hash
);
465 struct host_delete_ctx
{
469 int *skip_keys
; /* XXX split for host/ip? might want to ensure both */
470 struct sshkey
* const *keys
;
476 host_delete(struct hostkey_foreach_line
*l
, void *_ctx
)
478 struct host_delete_ctx
*ctx
= (struct host_delete_ctx
*)_ctx
;
479 int loglevel
= ctx
->quiet
? SYSLOG_LEVEL_DEBUG1
: SYSLOG_LEVEL_VERBOSE
;
482 if (l
->status
== HKF_STATUS_MATCHED
) {
483 if (l
->marker
!= MRK_NONE
) {
484 /* Don't remove CA and revocation lines */
485 fprintf(ctx
->out
, "%s\n", l
->line
);
489 /* XXX might need a knob for this later */
490 /* Don't remove RSA1 keys */
491 if (l
->key
->type
== KEY_RSA1
) {
492 fprintf(ctx
->out
, "%s\n", l
->line
);
497 * If this line contains one of the keys that we will be
498 * adding later, then don't change it and mark the key for
501 for (i
= 0; i
< ctx
->nkeys
; i
++) {
502 if (sshkey_equal(ctx
->keys
[i
], l
->key
)) {
503 ctx
->skip_keys
[i
] = 1;
504 fprintf(ctx
->out
, "%s\n", l
->line
);
505 debug3("%s: %s key already at %s:%ld", __func__
,
506 sshkey_type(l
->key
), l
->path
, l
->linenum
);
512 * Hostname matches and has no CA/revoke marker, delete it
513 * by *not* writing the line to ctx->out.
515 do_log2(loglevel
, "%s%s%s:%ld: Removed %s key for host %s",
516 ctx
->quiet
? __func__
: "", ctx
->quiet
? ": " : "",
517 l
->path
, l
->linenum
, sshkey_type(l
->key
), ctx
->host
);
521 /* Retain non-matching hosts and invalid lines when deleting */
522 if (l
->status
== HKF_STATUS_INVALID
) {
523 do_log2(loglevel
, "%s%s%s:%ld: invalid known_hosts entry",
524 ctx
->quiet
? __func__
: "", ctx
->quiet
? ": " : "",
525 l
->path
, l
->linenum
);
527 fprintf(ctx
->out
, "%s\n", l
->line
);
532 hostfile_replace_entries(const char *filename
, const char *host
, const char *ip
,
533 struct sshkey
**keys
, size_t nkeys
, int store_hash
, int quiet
, int hash_alg
)
535 int r
, fd
, oerrno
= 0;
536 int loglevel
= quiet
? SYSLOG_LEVEL_DEBUG1
: SYSLOG_LEVEL_VERBOSE
;
537 struct host_delete_ctx ctx
;
538 char *fp
, *temp
= NULL
, *back
= NULL
;
544 memset(&ctx
, 0, sizeof(ctx
));
547 if ((ctx
.skip_keys
= calloc(nkeys
, sizeof(*ctx
.skip_keys
))) == NULL
)
548 return SSH_ERR_ALLOC_FAIL
;
554 * Prepare temporary file for in-place deletion.
556 if ((r
= asprintf(&temp
, "%s.XXXXXXXXXXX", filename
)) < 0 ||
557 (r
= asprintf(&back
, "%s.old", filename
)) < 0) {
558 r
= SSH_ERR_ALLOC_FAIL
;
562 if ((fd
= mkstemp(temp
)) == -1) {
564 error("%s: mkstemp: %s", __func__
, strerror(oerrno
));
565 r
= SSH_ERR_SYSTEM_ERROR
;
568 if ((ctx
.out
= fdopen(fd
, "w")) == NULL
) {
571 error("%s: fdopen: %s", __func__
, strerror(oerrno
));
572 r
= SSH_ERR_SYSTEM_ERROR
;
576 /* Remove all entries for the specified host from the file */
577 if ((r
= hostkeys_foreach(filename
, host_delete
, &ctx
, host
, ip
,
578 HKF_WANT_PARSE_KEY
)) != 0) {
579 error("%s: hostkeys_foreach failed: %s", __func__
, ssh_err(r
));
583 /* Add the requested keys */
584 for (i
= 0; i
< nkeys
; i
++) {
585 if (ctx
.skip_keys
[i
])
587 if ((fp
= sshkey_fingerprint(keys
[i
], hash_alg
,
588 SSH_FP_DEFAULT
)) == NULL
) {
589 r
= SSH_ERR_ALLOC_FAIL
;
592 do_log2(loglevel
, "%s%sAdding new key for %s to %s: %s %s",
593 quiet
? __func__
: "", quiet
? ": " : "", host
, filename
,
594 sshkey_ssh_name(keys
[i
]), fp
);
596 if (!write_host_entry(ctx
.out
, host
, ip
, keys
[i
], store_hash
)) {
597 r
= SSH_ERR_INTERNAL_ERROR
;
606 /* Backup the original file and replace it with the temporary */
607 if (unlink(back
) == -1 && errno
!= ENOENT
) {
609 error("%s: unlink %.100s: %s", __func__
,
610 back
, strerror(errno
));
611 r
= SSH_ERR_SYSTEM_ERROR
;
614 if (link(filename
, back
) == -1) {
616 error("%s: link %.100s to %.100s: %s", __func__
,
617 filename
, back
, strerror(errno
));
618 r
= SSH_ERR_SYSTEM_ERROR
;
621 if (rename(temp
, filename
) == -1) {
623 error("%s: rename \"%s\" to \"%s\": %s", __func__
,
624 temp
, filename
, strerror(errno
));
625 r
= SSH_ERR_SYSTEM_ERROR
;
629 /* No changes made; just delete the temporary file */
630 if (unlink(temp
) != 0)
631 error("%s: unlink \"%s\": %s", __func__
,
632 temp
, strerror(errno
));
638 if (temp
!= NULL
&& r
!= 0)
646 if (r
== SSH_ERR_SYSTEM_ERROR
)
652 match_maybe_hashed(const char *host
, const char *names
, int *was_hashed
)
654 int hashed
= *names
== HASH_DELIM
;
655 const char *hashed_host
;
656 size_t nlen
= strlen(names
);
658 if (was_hashed
!= NULL
)
659 *was_hashed
= hashed
;
661 if ((hashed_host
= host_hash(host
, names
, nlen
)) == NULL
)
663 return nlen
== strlen(hashed_host
) &&
664 strncmp(hashed_host
, names
, nlen
) == 0;
666 return match_hostname(host
, names
) == 1;
670 hostkeys_foreach(const char *path
, hostkeys_foreach_fn
*callback
, void *ctx
,
671 const char *host
, const char *ip
, u_int options
)
674 char line
[8192], oline
[8192], ktype
[128];
680 struct hostkey_foreach_line lineinfo
;
683 memset(&lineinfo
, 0, sizeof(lineinfo
));
684 if (host
== NULL
&& (options
& HKF_WANT_MATCH
) != 0)
685 return SSH_ERR_INVALID_ARGUMENT
;
686 if ((f
= fopen(path
, "r")) == NULL
)
687 return SSH_ERR_SYSTEM_ERROR
;
689 debug3("%s: reading file \"%s\"", __func__
, path
);
690 while (read_keyfile_line(f
, path
, line
, sizeof(line
), &linenum
) == 0) {
691 line
[strcspn(line
, "\n")] = '\0';
692 strlcpy(oline
, line
, sizeof(oline
));
694 sshkey_free(lineinfo
.key
);
695 memset(&lineinfo
, 0, sizeof(lineinfo
));
696 lineinfo
.path
= path
;
697 lineinfo
.linenum
= linenum
;
698 lineinfo
.line
= oline
;
699 lineinfo
.marker
= MRK_NONE
;
700 lineinfo
.status
= HKF_STATUS_OK
;
701 lineinfo
.keytype
= KEY_UNSPEC
;
703 /* Skip any leading whitespace, comments and empty lines. */
704 for (cp
= line
; *cp
== ' ' || *cp
== '\t'; cp
++)
706 if (!*cp
|| *cp
== '#' || *cp
== '\n') {
707 if ((options
& HKF_WANT_MATCH
) == 0) {
708 lineinfo
.status
= HKF_STATUS_COMMENT
;
709 if ((r
= callback(&lineinfo
, ctx
)) != 0)
715 if ((lineinfo
.marker
= check_markers(&cp
)) == MRK_ERROR
) {
716 verbose("%s: invalid marker at %s:%lu",
717 __func__
, path
, linenum
);
718 if ((options
& HKF_WANT_MATCH
) == 0)
723 /* Find the end of the host name portion. */
724 for (cp2
= cp
; *cp2
&& *cp2
!= ' ' && *cp2
!= '\t'; cp2
++)
729 /* Check if the host name matches. */
731 if ((s
= match_maybe_hashed(host
, lineinfo
.hosts
,
733 debug2("%s: %s:%ld: bad host hash \"%.32s\"",
734 __func__
, path
, linenum
, lineinfo
.hosts
);
738 lineinfo
.status
= HKF_STATUS_MATCHED
;
739 lineinfo
.match
|= HKF_MATCH_HOST
|
740 (hashed
? HKF_MATCH_HOST_HASHED
: 0);
742 /* Try matching IP address if supplied */
744 if ((s
= match_maybe_hashed(ip
, lineinfo
.hosts
,
746 debug2("%s: %s:%ld: bad ip hash "
747 "\"%.32s\"", __func__
, path
,
748 linenum
, lineinfo
.hosts
);
752 lineinfo
.status
= HKF_STATUS_MATCHED
;
753 lineinfo
.match
|= HKF_MATCH_IP
|
754 (hashed
? HKF_MATCH_IP_HASHED
: 0);
758 * Skip this line if host matching requested and
759 * neither host nor address matched.
761 if ((options
& HKF_WANT_MATCH
) != 0 &&
762 lineinfo
.status
!= HKF_STATUS_MATCHED
)
766 /* Got a match. Skip host name and any following whitespace */
767 for (; *cp2
== ' ' || *cp2
== '\t'; cp2
++)
769 if (*cp2
== '\0' || *cp2
== '#') {
770 debug2("%s:%ld: truncated before key type",
774 lineinfo
.rawkey
= cp
= cp2
;
776 if ((options
& HKF_WANT_PARSE_KEY
) != 0) {
778 * Extract the key from the line. This will skip
779 * any leading whitespace. Ignore badly formatted
782 if ((lineinfo
.key
= sshkey_new(KEY_UNSPEC
)) == NULL
) {
783 error("%s: sshkey_new failed", __func__
);
784 r
= SSH_ERR_ALLOC_FAIL
;
787 if (!hostfile_read_key(&cp
, &kbits
, lineinfo
.key
)) {
789 sshkey_free(lineinfo
.key
);
790 lineinfo
.key
= sshkey_new(KEY_RSA1
);
791 if (lineinfo
.key
== NULL
) {
792 error("%s: sshkey_new fail", __func__
);
793 r
= SSH_ERR_ALLOC_FAIL
;
796 if (!hostfile_read_key(&cp
, &kbits
,
803 lineinfo
.keytype
= lineinfo
.key
->type
;
804 lineinfo
.comment
= cp
;
806 /* Extract and parse key type */
807 l
= strcspn(lineinfo
.rawkey
, " \t");
808 if (l
<= 1 || l
>= sizeof(ktype
) ||
809 lineinfo
.rawkey
[l
] == '\0')
811 memcpy(ktype
, lineinfo
.rawkey
, l
);
813 lineinfo
.keytype
= sshkey_type_from_name(ktype
);
816 * Assume RSA1 if the first component is a short
819 if (lineinfo
.keytype
== KEY_UNSPEC
&& l
< 8 &&
820 strspn(ktype
, "0123456789") == l
)
821 lineinfo
.keytype
= KEY_RSA1
;
824 * Check that something other than whitespace follows
825 * the key type. This won't catch all corruption, but
826 * it does catch trivial truncation.
828 cp2
+= l
; /* Skip past key type */
829 for (; *cp2
== ' ' || *cp2
== '\t'; cp2
++)
831 if (*cp2
== '\0' || *cp2
== '#') {
832 debug2("%s:%ld: truncated after key type",
834 lineinfo
.keytype
= KEY_UNSPEC
;
836 if (lineinfo
.keytype
== KEY_UNSPEC
) {
838 sshkey_free(lineinfo
.key
);
840 lineinfo
.status
= HKF_STATUS_INVALID
;
841 if ((r
= callback(&lineinfo
, ctx
)) != 0)
846 if ((r
= callback(&lineinfo
, ctx
)) != 0)
849 sshkey_free(lineinfo
.key
);