2 * Copyright (c) 2009-2014 by Daniel Stenberg
5 * Redistribution and use in source and binary forms,
6 * with or without modification, are permitted provided
7 * that the following conditions are met:
9 * Redistributions of source code must retain the above
10 * copyright notice, this list of conditions and the
11 * following disclaimer.
13 * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
18 * Neither the name of the copyright holder nor the names
19 * of any other contributors may be used to endorse or
20 * promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
35 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
39 #include "libssh2_priv.h"
43 struct list_node node
;
44 char *name
; /* points to the name or the hash (allocated) */
45 size_t name_len
; /* needed for hashed data */
46 int port
; /* if non-zero, a specific port this key is for on this
48 int typemask
; /* plain, sha1, custom, ... */
49 char *salt
; /* points to binary salt (allocated) */
50 size_t salt_len
; /* size of salt */
51 char *key
; /* the (allocated) associated key. This is kept base64
53 char *key_type_name
; /* the (allocated) key type name */
54 size_t key_type_len
; /* size of key_type_name */
55 char *comment
; /* the (allocated) optional comment text, may be
57 size_t comment_len
; /* the size of comment */
59 /* this is the struct we expose externally */
60 struct libssh2_knownhost external
;
63 struct _LIBSSH2_KNOWNHOSTS
65 LIBSSH2_SESSION
*session
; /* the session this "belongs to" */
66 struct list_head head
;
69 static void free_host(LIBSSH2_SESSION
*session
, struct known_host
*entry
)
73 LIBSSH2_FREE(session
, entry
->comment
);
74 if (entry
->key_type_name
)
75 LIBSSH2_FREE(session
, entry
->key_type_name
);
77 LIBSSH2_FREE(session
, entry
->key
);
79 LIBSSH2_FREE(session
, entry
->salt
);
81 LIBSSH2_FREE(session
, entry
->name
);
82 LIBSSH2_FREE(session
, entry
);
87 * libssh2_knownhost_init
89 * Init a collection of known hosts. Returns the pointer to a collection.
92 LIBSSH2_API LIBSSH2_KNOWNHOSTS
*
93 libssh2_knownhost_init(LIBSSH2_SESSION
*session
)
95 LIBSSH2_KNOWNHOSTS
*knh
=
96 LIBSSH2_ALLOC(session
, sizeof(struct _LIBSSH2_KNOWNHOSTS
));
99 _libssh2_error(session
, LIBSSH2_ERROR_ALLOC
,
100 "Unable to allocate memory for known-hosts "
105 knh
->session
= session
;
107 _libssh2_list_init(&knh
->head
);
112 #define KNOWNHOST_MAGIC 0xdeadcafe
114 * knownhost_to_external()
116 * Copies data from the internal to the external representation struct.
119 static struct libssh2_knownhost
*knownhost_to_external(struct known_host
*node
)
121 struct libssh2_knownhost
*ext
= &node
->external
;
123 ext
->magic
= KNOWNHOST_MAGIC
;
125 ext
->name
= ((node
->typemask
& LIBSSH2_KNOWNHOST_TYPE_MASK
) ==
126 LIBSSH2_KNOWNHOST_TYPE_PLAIN
)? node
->name
:NULL
;
127 ext
->key
= node
->key
;
128 ext
->typemask
= node
->typemask
;
134 knownhost_add(LIBSSH2_KNOWNHOSTS
*hosts
,
135 const char *host
, const char *salt
,
136 const char *key_type_name
, size_t key_type_len
,
137 const char *key
, size_t keylen
,
138 const char *comment
, size_t commentlen
,
139 int typemask
, struct libssh2_knownhost
**store
)
141 struct known_host
*entry
;
142 size_t hostlen
= strlen(host
);
147 /* make sure we have a key type set */
148 if(!(typemask
& LIBSSH2_KNOWNHOST_KEY_MASK
))
149 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_INVAL
,
152 if(!(entry
= LIBSSH2_CALLOC(hosts
->session
, sizeof(struct known_host
))))
153 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
154 "Unable to allocate memory for known host "
157 entry
->typemask
= typemask
;
159 switch(entry
->typemask
& LIBSSH2_KNOWNHOST_TYPE_MASK
) {
160 case LIBSSH2_KNOWNHOST_TYPE_PLAIN
:
161 case LIBSSH2_KNOWNHOST_TYPE_CUSTOM
:
162 entry
->name
= LIBSSH2_ALLOC(hosts
->session
, hostlen
+1);
164 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
165 "Unable to allocate memory for host name");
168 memcpy(entry
->name
, host
, hostlen
+1);
169 entry
->name_len
= hostlen
;
171 case LIBSSH2_KNOWNHOST_TYPE_SHA1
:
172 rc
= libssh2_base64_decode(hosts
->session
, &ptr
, &ptrlen
,
177 entry
->name_len
= ptrlen
;
179 rc
= libssh2_base64_decode(hosts
->session
, &ptr
, &ptrlen
,
184 entry
->salt_len
= ptrlen
;
187 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
188 "Unknown host name type");
192 if(typemask
& LIBSSH2_KNOWNHOST_KEYENC_BASE64
) {
193 /* the provided key is base64 encoded already */
195 keylen
= strlen(key
);
196 entry
->key
= LIBSSH2_ALLOC(hosts
->session
, keylen
+1);
198 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
199 "Unable to allocate memory for key");
202 memcpy(entry
->key
, key
, keylen
+1);
203 entry
->key
[keylen
]=0; /* force a terminating zero trailer */
206 /* key is raw, we base64 encode it and store it as such */
207 size_t nlen
= _libssh2_base64_encode(hosts
->session
, key
, keylen
,
210 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
211 "Unable to allocate memory for "
212 "base64-encoded key");
219 if (key_type_name
&& ((typemask
& LIBSSH2_KNOWNHOST_KEY_MASK
) ==
220 LIBSSH2_KNOWNHOST_KEY_UNKNOWN
)) {
221 entry
->key_type_name
= LIBSSH2_ALLOC(hosts
->session
, key_type_len
+1);
222 if (!entry
->key_type_name
) {
223 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
224 "Unable to allocate memory for key type");
227 memcpy(entry
->key_type_name
, key_type_name
, key_type_len
);
228 entry
->key_type_name
[key_type_len
]=0;
229 entry
->key_type_len
= key_type_len
;
233 entry
->comment
= LIBSSH2_ALLOC(hosts
->session
, commentlen
+1);
234 if(!entry
->comment
) {
235 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
236 "Unable to allocate memory for comment");
239 memcpy(entry
->comment
, comment
, commentlen
+1);
240 entry
->comment
[commentlen
]=0; /* force a terminating zero trailer */
241 entry
->comment_len
= commentlen
;
244 entry
->comment
= NULL
;
247 /* add this new host to the big list of known hosts */
248 _libssh2_list_add(&hosts
->head
, &entry
->node
);
251 *store
= knownhost_to_external(entry
);
253 return LIBSSH2_ERROR_NONE
;
255 free_host(hosts
->session
, entry
);
260 * libssh2_knownhost_add
262 * Add a host and its associated key to the collection of known hosts.
264 * The 'type' argument specifies on what format the given host and keys are:
266 * plain - ascii "hostname.domain.tld"
267 * sha1 - SHA1(<salt> <host>) base64-encoded!
268 * custom - another hash
270 * If 'sha1' is selected as type, the salt must be provided to the salt
271 * argument. This too base64 encoded.
273 * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
274 * a custom type is used, salt is ignored and you must provide the host
275 * pre-hashed when checking for it in the libssh2_knownhost_check() function.
277 * The keylen parameter may be omitted (zero) if the key is provided as a
278 * NULL-terminated base64-encoded string.
282 libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS
*hosts
,
283 const char *host
, const char *salt
,
284 const char *key
, size_t keylen
,
285 int typemask
, struct libssh2_knownhost
**store
)
287 return knownhost_add(hosts
, host
, salt
, NULL
, 0, key
, keylen
, NULL
,
293 * libssh2_knownhost_addc
295 * Add a host and its associated key to the collection of known hosts.
297 * Takes a comment argument that may be NULL. A NULL comment indicates
298 * there is no comment and the entry will end directly after the key
299 * when written out to a file. An empty string "" comment will indicate an
300 * empty comment which will cause a single space to be written after the key.
302 * The 'type' argument specifies on what format the given host and keys are:
304 * plain - ascii "hostname.domain.tld"
305 * sha1 - SHA1(<salt> <host>) base64-encoded!
306 * custom - another hash
308 * If 'sha1' is selected as type, the salt must be provided to the salt
309 * argument. This too base64 encoded.
311 * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
312 * a custom type is used, salt is ignored and you must provide the host
313 * pre-hashed when checking for it in the libssh2_knownhost_check() function.
315 * The keylen parameter may be omitted (zero) if the key is provided as a
316 * NULL-terminated base64-encoded string.
320 libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS
*hosts
,
321 const char *host
, const char *salt
,
322 const char *key
, size_t keylen
,
323 const char *comment
, size_t commentlen
,
324 int typemask
, struct libssh2_knownhost
**store
)
326 return knownhost_add(hosts
, host
, salt
, NULL
, 0, key
, keylen
,
327 comment
, commentlen
, typemask
, store
);
333 * Check a host and its associated key against the collection of known hosts.
335 * The typemask is the type/format of the given host name and key
337 * plain - ascii "hostname.domain.tld"
338 * sha1 - NOT SUPPORTED AS INPUT
339 * custom - prehashed base64 encoded. Note that this cannot use any salts.
343 * LIBSSH2_KNOWNHOST_CHECK_FAILURE
344 * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
345 * LIBSSH2_KNOWNHOST_CHECK_MATCH
346 * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
349 knownhost_check(LIBSSH2_KNOWNHOSTS
*hosts
,
350 const char *hostp
, int port
,
351 const char *key
, size_t keylen
,
353 struct libssh2_knownhost
**ext
)
355 struct known_host
*node
;
356 struct known_host
*badkey
= NULL
;
357 int type
= typemask
& LIBSSH2_KNOWNHOST_TYPE_MASK
;
358 char *keyalloc
= NULL
;
359 int rc
= LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
;
360 char hostbuff
[270]; /* most host names can't be longer than like 256 */
362 int numcheck
; /* number of host combos to check */
365 if(type
== LIBSSH2_KNOWNHOST_TYPE_SHA1
)
366 /* we can't work with a sha1 as given input */
367 return LIBSSH2_KNOWNHOST_CHECK_MISMATCH
;
369 /* if a port number is given, check for a '[host]:port' first before the
372 int len
= snprintf(hostbuff
, sizeof(hostbuff
), "[%s]:%d", hostp
, port
);
373 if (len
< 0 || len
>= (int)sizeof(hostbuff
)) {
374 _libssh2_error(hosts
->session
,
375 LIBSSH2_ERROR_BUFFER_TOO_SMALL
,
376 "Known-host write buffer too small");
377 return LIBSSH2_KNOWNHOST_CHECK_FAILURE
;
380 numcheck
= 2; /* check both combos, start with this */
384 numcheck
= 1; /* only check this host version */
387 if(!(typemask
& LIBSSH2_KNOWNHOST_KEYENC_BASE64
)) {
388 /* we got a raw key input, convert it to base64 for the checks below */
389 size_t nlen
= _libssh2_base64_encode(hosts
->session
, key
, keylen
,
392 _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
393 "Unable to allocate memory for base64-encoded "
395 return LIBSSH2_KNOWNHOST_CHECK_FAILURE
;
398 /* make the key point to this */
403 node
= _libssh2_list_first(&hosts
->head
);
405 switch(node
->typemask
& LIBSSH2_KNOWNHOST_TYPE_MASK
) {
406 case LIBSSH2_KNOWNHOST_TYPE_PLAIN
:
407 if(type
== LIBSSH2_KNOWNHOST_TYPE_PLAIN
)
408 match
= !strcmp(host
, node
->name
);
410 case LIBSSH2_KNOWNHOST_TYPE_CUSTOM
:
411 if(type
== LIBSSH2_KNOWNHOST_TYPE_CUSTOM
)
412 match
= !strcmp(host
, node
->name
);
414 case LIBSSH2_KNOWNHOST_TYPE_SHA1
:
415 if(type
== LIBSSH2_KNOWNHOST_TYPE_PLAIN
) {
416 /* when we have the sha1 version stored, we can use a
417 plain input to produce a hash to compare with the
420 unsigned char hash
[SHA_DIGEST_LENGTH
];
421 libssh2_hmac_ctx ctx
;
422 libssh2_hmac_ctx_init(ctx
);
424 if(SHA_DIGEST_LENGTH
!= node
->name_len
) {
425 /* the name hash length must be the sha1 size or
429 libssh2_hmac_sha1_init(&ctx
, (unsigned char *)node
->salt
,
431 libssh2_hmac_update(ctx
, (unsigned char *)host
,
433 libssh2_hmac_final(ctx
, hash
);
434 libssh2_hmac_cleanup(&ctx
);
436 if(!memcmp(hash
, node
->name
, SHA_DIGEST_LENGTH
))
437 /* this is a node we're interested in */
441 default: /* unsupported type */
445 int host_key_type
= typemask
& LIBSSH2_KNOWNHOST_KEY_MASK
;
447 node
->typemask
& LIBSSH2_KNOWNHOST_KEY_MASK
;
448 /* match on key type as follows:
449 - never match on an unknown key type
450 - if key_type is set to zero, ignore it an match always
451 - otherwise match when both key types are equal
453 if ( (host_key_type
!= LIBSSH2_KNOWNHOST_KEY_UNKNOWN
) &&
454 ( (host_key_type
== 0) ||
455 (host_key_type
== known_key_type
) ) ) {
456 /* host name and key type match, now compare the keys */
457 if(!strcmp(key
, node
->key
)) {
460 *ext
= knownhost_to_external(node
);
462 rc
= LIBSSH2_KNOWNHOST_CHECK_MATCH
;
466 /* remember the first node that had a host match but a
467 failed key match since we continue our search from
473 match
= 0; /* don't count this as a match anymore */
475 node
= _libssh2_list_next(&node
->node
);
478 } while(!match
&& --numcheck
);
483 *ext
= knownhost_to_external(badkey
);
484 rc
= LIBSSH2_KNOWNHOST_CHECK_MISMATCH
;
488 LIBSSH2_FREE(hosts
->session
, keyalloc
);
494 * libssh2_knownhost_check
496 * Check a host and its associated key against the collection of known hosts.
498 * The typemask is the type/format of the given host name and key
500 * plain - ascii "hostname.domain.tld"
501 * sha1 - NOT SUPPORTED AS INPUT
502 * custom - prehashed base64 encoded. Note that this cannot use any salts.
506 * LIBSSH2_KNOWNHOST_CHECK_FAILURE
507 * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
508 * LIBSSH2_KNOWNHOST_CHECK_MATCH
509 * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
512 libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS
*hosts
,
513 const char *hostp
, const char *key
, size_t keylen
,
515 struct libssh2_knownhost
**ext
)
517 return knownhost_check(hosts
, hostp
, -1, key
, keylen
,
522 * libssh2_knownhost_checkp
524 * Check a host+port and its associated key against the collection of known
527 * Note that if 'port' is specified as greater than zero, the check function
528 * will be able to check for a dedicated key for this particular host+port
529 * combo, and if 'port' is negative it only checks for the generic host key.
531 * The typemask is the type/format of the given host name and key
533 * plain - ascii "hostname.domain.tld"
534 * sha1 - NOT SUPPORTED AS INPUT
535 * custom - prehashed base64 encoded. Note that this cannot use any salts.
539 * LIBSSH2_KNOWNHOST_CHECK_FAILURE
540 * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
541 * LIBSSH2_KNOWNHOST_CHECK_MATCH
542 * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
545 libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS
*hosts
,
546 const char *hostp
, int port
,
547 const char *key
, size_t keylen
,
549 struct libssh2_knownhost
**ext
)
551 return knownhost_check(hosts
, hostp
, port
, key
, keylen
,
557 * libssh2_knownhost_del
559 * Remove a host from the collection of known hosts.
563 libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS
*hosts
,
564 struct libssh2_knownhost
*entry
)
566 struct known_host
*node
;
568 /* check that this was retrieved the right way or get out */
569 if(!entry
|| (entry
->magic
!= KNOWNHOST_MAGIC
))
570 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_INVAL
,
571 "Invalid host information");
573 /* get the internal node pointer */
576 /* unlink from the list of all hosts */
577 _libssh2_list_remove(&node
->node
);
579 /* clear the struct now since the memory in which it is allocated is
580 about to be freed! */
581 memset(entry
, 0, sizeof(struct libssh2_knownhost
));
583 /* free all resources */
584 free_host(hosts
->session
, node
);
590 * libssh2_knownhost_free
592 * Free an entire collection of known hosts.
596 libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS
*hosts
)
598 struct known_host
*node
;
599 struct known_host
*next
;
601 for(node
= _libssh2_list_first(&hosts
->head
); node
; node
= next
) {
602 next
= _libssh2_list_next(&node
->node
);
603 free_host(hosts
->session
, node
);
605 LIBSSH2_FREE(hosts
->session
, hosts
);
609 /* old style plain text: [name]([,][name])*
611 for the sake of simplicity, we add them as separate hosts with the same
614 static int oldstyle_hostline(LIBSSH2_KNOWNHOSTS
*hosts
,
615 const char *host
, size_t hostlen
,
616 const char *key_type_name
, size_t key_type_len
,
617 const char *key
, size_t keylen
, int key_type
,
618 const char *comment
, size_t commentlen
)
622 const char *name
= host
+ hostlen
;
625 return _libssh2_error(hosts
->session
,
626 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
627 "Failed to parse known_hosts line "
634 /* when we get the the start or see a comma coming up, add the host
635 name to the collection */
636 if((name
== host
) || (*(name
-1) == ',')) {
640 /* make sure we don't overflow the buffer */
641 if(namelen
>= sizeof(hostbuf
)-1)
642 return _libssh2_error(hosts
->session
,
643 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
644 "Failed to parse known_hosts line "
645 "(unexpected length)");
647 /* copy host name to the temp buffer and zero terminate */
648 memcpy(hostbuf
, name
, namelen
);
651 rc
= knownhost_add(hosts
, hostbuf
, NULL
,
652 key_type_name
, key_type_len
,
655 key_type
| LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
656 LIBSSH2_KNOWNHOST_KEYENC_BASE64
, NULL
);
662 --name
; /* skip comma */
670 /* |1|[salt]|[hash] */
671 static int hashed_hostline(LIBSSH2_KNOWNHOSTS
*hosts
,
672 const char *host
, size_t hostlen
,
673 const char *key_type_name
, size_t key_type_len
,
674 const char *key
, size_t keylen
, int key_type
,
675 const char *comment
, size_t commentlen
)
681 const char *salt
= &host
[3]; /* skip the magic marker */
682 hostlen
-= 3; /* deduct the marker */
684 /* this is where the salt starts, find the end of it */
685 for(p
= salt
; *p
&& (*p
!= '|'); p
++)
689 const char *hash
= NULL
;
690 size_t saltlen
= p
- salt
;
691 if(saltlen
>= (sizeof(saltbuf
)-1)) /* weird length */
692 return _libssh2_error(hosts
->session
,
693 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
694 "Failed to parse known_hosts line "
695 "(unexpectedly long salt)");
697 memcpy(saltbuf
, salt
, saltlen
);
698 saltbuf
[saltlen
] = 0; /* zero terminate */
699 salt
= saltbuf
; /* point to the stack based buffer */
701 hash
= p
+1; /* the host hash is after the separator */
703 /* now make the host point to the hash */
705 hostlen
-= saltlen
+1; /* deduct the salt and separator */
707 /* check that the lengths seem sensible */
708 if(hostlen
>= sizeof(hostbuf
)-1)
709 return _libssh2_error(hosts
->session
,
710 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
711 "Failed to parse known_hosts line "
712 "(unexpected length)");
714 memcpy(hostbuf
, host
, hostlen
);
717 return knownhost_add(hosts
, hostbuf
, salt
,
718 key_type_name
, key_type_len
,
721 key_type
| LIBSSH2_KNOWNHOST_TYPE_SHA1
|
722 LIBSSH2_KNOWNHOST_KEYENC_BASE64
, NULL
);
725 return 0; /* XXX: This should be an error, shouldn't it? */
731 * Parse a single known_host line pre-split into host and key.
733 * The key part may include an optional comment which will be parsed here
734 * for ssh-rsa and ssh-dsa keys. Comments in other key types aren't handled.
736 * The function assumes new-lines have already been removed from the arguments.
738 static int hostline(LIBSSH2_KNOWNHOSTS
*hosts
,
739 const char *host
, size_t hostlen
,
740 const char *key
, size_t keylen
)
742 const char *comment
= NULL
;
743 const char *key_type_name
= NULL
;
744 size_t commentlen
= 0;
745 size_t key_type_len
= 0;
748 /* make some checks that the lengths seem sensible */
750 return _libssh2_error(hosts
->session
,
751 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
752 "Failed to parse known_hosts line "
756 case '0': case '1': case '2': case '3': case '4':
757 case '5': case '6': case '7': case '8': case '9':
758 key_type
= LIBSSH2_KNOWNHOST_KEY_RSA1
;
760 /* Note that the old-style keys (RSA1) aren't truly base64, but we
761 * claim it is for now since we can get away with strcmp()ing the
762 * entire anything anyway! We need to check and fix these to make them
769 while (keylen
&& *key
&&
770 (*key
!= ' ') && (*key
!= '\t')) {
774 key_type_len
= key
- key_type_name
;
776 if (!strncmp(key_type_name
, "ssh-dss", key_type_len
))
777 key_type
= LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
778 else if (!strncmp(key_type_name
, "ssh-rsa", key_type_len
))
779 key_type
= LIBSSH2_KNOWNHOST_KEY_SSHRSA
;
781 key_type
= LIBSSH2_KNOWNHOST_KEY_UNKNOWN
;
783 /* skip whitespaces */
784 while((*key
==' ') || (*key
== '\t')) {
793 while(commentlen
&& *comment
&&
794 (*comment
!= ' ') && (*comment
!= '\t')) {
799 /* reduce key by comment length */
800 keylen
-= commentlen
;
802 /* Distinguish empty comment (a space) from no comment (no space) */
806 /* skip whitespaces */
807 while(commentlen
&& *comment
&&
808 ((*comment
==' ') || (*comment
== '\t'))) {
815 /* Figure out host format */
816 if((hostlen
>2) && memcmp(host
, "|1|", 3)) {
817 /* old style plain text: [name]([,][name])*
819 for the sake of simplicity, we add them as separate hosts with the
822 return oldstyle_hostline(hosts
, host
, hostlen
, key_type_name
,
823 key_type_len
, key
, keylen
, key_type
,
824 comment
, commentlen
);
827 /* |1|[salt]|[hash] */
828 return hashed_hostline(hosts
, host
, hostlen
, key_type_name
,
829 key_type_len
, key
, keylen
, key_type
,
830 comment
, commentlen
);
835 * libssh2_knownhost_readline()
837 * Pass in a line of a file of 'type'.
839 * LIBSSH2_KNOWNHOST_FILE_OPENSSH is the only supported type.
841 * OpenSSH line format:
845 * Where the two parts can be created like:
847 * <host> can be either
851 * [name] optionally followed by [,name] one or more times
856 * <key> can be one of:
857 * [RSA bits] [e] [n as a decimal number]
858 * 'ssh-dss' [base64-encoded-key]
859 * 'ssh-rsa' [base64-encoded-key]
863 libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS
*hosts
,
864 const char *line
, size_t len
, int type
)
873 if(type
!= LIBSSH2_KNOWNHOST_FILE_OPENSSH
)
874 return _libssh2_error(hosts
->session
,
875 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
876 "Unsupported type of known-host information "
881 /* skip leading whitespaces */
882 while(len
&& ((*cp
==' ') || (*cp
== '\t'))) {
887 if(!len
|| !*cp
|| (*cp
== '#') || (*cp
== '\n'))
888 /* comment or empty line */
889 return LIBSSH2_ERROR_NONE
;
891 /* the host part starts here */
894 /* move over the host to the separator */
895 while(len
&& *cp
&& (*cp
!=' ') && (*cp
!= '\t')) {
900 hostlen
= cp
- hostp
;
902 /* the key starts after the whitespaces */
903 while(len
&& *cp
&& ((*cp
==' ') || (*cp
== '\t'))) {
908 if(!*cp
|| !len
) /* illegal line */
909 return _libssh2_error(hosts
->session
,
910 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
911 "Failed to parse known_hosts line");
913 keyp
= cp
; /* the key starts here */
916 /* check if the line (key) ends with a newline and if so kill it */
917 while(len
&& *cp
&& (*cp
!= '\n')) {
922 /* zero terminate where the newline is */
924 keylen
--; /* don't include this in the count */
926 /* deal with this one host+key line */
927 rc
= hostline(hosts
, hostp
, hostlen
, keyp
, keylen
);
929 return rc
; /* failed */
931 return LIBSSH2_ERROR_NONE
; /* success */
935 * libssh2_knownhost_readfile
937 * Read hosts+key pairs from a given file.
939 * Returns a negative value for error or number of successfully added hosts.
944 libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS
*hosts
,
945 const char *filename
, int type
)
951 if(type
!= LIBSSH2_KNOWNHOST_FILE_OPENSSH
)
952 return _libssh2_error(hosts
->session
,
953 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
954 "Unsupported type of known-host information "
957 file
= fopen(filename
, "r");
959 while(fgets(buf
, sizeof(buf
), file
)) {
960 if(libssh2_knownhost_readline(hosts
, buf
, strlen(buf
), type
)) {
961 num
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_KNOWN_HOSTS
,
962 "Failed to parse known hosts file");
970 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_FILE
,
971 "Failed to open file");
977 * knownhost_writeline()
979 * Ask libssh2 to convert a known host to an output line for storage.
981 * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given
982 * output buffer is too small to hold the desired output. The 'outlen' field
983 * will then contain the size libssh2 wanted to store, which then is the
984 * smallest sufficient buffer it would require.
988 knownhost_writeline(LIBSSH2_KNOWNHOSTS
*hosts
,
989 struct known_host
*node
,
990 char *buf
, size_t buflen
,
991 size_t *outlen
, int type
)
993 size_t required_size
;
995 const char *key_type_name
;
998 /* we only support this single file type for now, bail out on all other
1000 if(type
!= LIBSSH2_KNOWNHOST_FILE_OPENSSH
)
1001 return _libssh2_error(hosts
->session
,
1002 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
1003 "Unsupported type of known-host information "
1006 switch(node
->typemask
& LIBSSH2_KNOWNHOST_KEY_MASK
) {
1007 case LIBSSH2_KNOWNHOST_KEY_RSA1
:
1008 key_type_name
= NULL
;
1011 case LIBSSH2_KNOWNHOST_KEY_SSHRSA
:
1012 key_type_name
= "ssh-rsa";
1015 case LIBSSH2_KNOWNHOST_KEY_SSHDSS
:
1016 key_type_name
= "ssh-dss";
1019 case LIBSSH2_KNOWNHOST_KEY_UNKNOWN
:
1020 key_type_name
= node
->key_type_name
;
1021 if (key_type_name
) {
1022 key_type_len
= node
->key_type_len
;
1025 /* otherwise fallback to default and error */
1027 return _libssh2_error(hosts
->session
,
1028 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
1029 "Unsupported type of known-host entry");
1032 /* When putting together the host line there are three aspects to consider:
1033 - Hashed (SHA1) or unhashed hostname
1034 - key name or no key name (RSA1)
1035 - comment or no comment
1037 This means there are 2^3 different formats:
1038 ("|1|%s|%s %s %s %s\n", salt, hashed_host, key_name, key, comment)
1039 ("|1|%s|%s %s %s\n", salt, hashed_host, key_name, key)
1040 ("|1|%s|%s %s %s\n", salt, hashed_host, key, comment)
1041 ("|1|%s|%s %s\n", salt, hashed_host, key)
1042 ("%s %s %s %s\n", host, key_name, key, comment)
1043 ("%s %s %s\n", host, key_name, key)
1044 ("%s %s %s\n", host, key, comment)
1045 ("%s %s\n", host, key)
1047 Even if the buffer is too small, we have to set outlen to the number of
1048 characters the complete line would have taken. We also don't write
1049 anything to the buffer unless we are sure we can write everything to the
1052 required_size
= strlen(node
->key
);
1055 required_size
+= key_type_len
+ 1; /* ' ' = 1 */
1057 required_size
+= node
->comment_len
+ 1; /* ' ' = 1 */
1059 if((node
->typemask
& LIBSSH2_KNOWNHOST_TYPE_MASK
) ==
1060 LIBSSH2_KNOWNHOST_TYPE_SHA1
) {
1062 size_t name_base64_len
;
1064 size_t salt_base64_len
;
1066 name_base64_len
= _libssh2_base64_encode(hosts
->session
, node
->name
,
1067 node
->name_len
, &namealloc
);
1068 if(!name_base64_len
)
1069 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
1070 "Unable to allocate memory for "
1071 "base64-encoded host name");
1073 salt_base64_len
= _libssh2_base64_encode(hosts
->session
,
1074 node
->salt
, node
->salt_len
,
1076 if(!salt_base64_len
) {
1077 LIBSSH2_FREE(hosts
->session
, namealloc
);
1078 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_ALLOC
,
1079 "Unable to allocate memory for "
1080 "base64-encoded salt");
1083 required_size
+= salt_base64_len
+ name_base64_len
+ 7;
1084 /* |1| + | + ' ' + \n + \0 = 7 */
1086 if(required_size
<= buflen
) {
1087 if(node
->comment
&& key_type_len
)
1088 snprintf(buf
, buflen
, "|1|%s|%s %s %s %s\n", saltalloc
,
1089 namealloc
, key_type_name
, node
->key
, node
->comment
);
1090 else if (node
->comment
)
1091 snprintf(buf
, buflen
, "|1|%s|%s %s %s\n", saltalloc
, namealloc
,
1092 node
->key
, node
->comment
);
1093 else if (key_type_len
)
1094 snprintf(buf
, buflen
, "|1|%s|%s %s %s\n", saltalloc
, namealloc
,
1095 key_type_name
, node
->key
);
1097 snprintf(buf
, buflen
, "|1|%s|%s %s\n", saltalloc
, namealloc
,
1101 LIBSSH2_FREE(hosts
->session
, namealloc
);
1102 LIBSSH2_FREE(hosts
->session
, saltalloc
);
1105 required_size
+= node
->name_len
+ 3;
1106 /* ' ' + '\n' + \0 = 3 */
1108 if(required_size
<= buflen
) {
1109 if(node
->comment
&& key_type_len
)
1110 snprintf(buf
, buflen
, "%s %s %s %s\n", node
->name
,
1111 key_type_name
, node
->key
, node
->comment
);
1112 else if (node
->comment
)
1113 snprintf(buf
, buflen
, "%s %s %s\n", node
->name
, node
->key
,
1115 else if (key_type_len
)
1116 snprintf(buf
, buflen
, "%s %s %s\n", node
->name
, key_type_name
,
1119 snprintf(buf
, buflen
, "%s %s\n", node
->name
, node
->key
);
1123 /* we report the full length of the data with the trailing zero excluded */
1124 *outlen
= required_size
-1;
1126 if(required_size
<= buflen
)
1127 return LIBSSH2_ERROR_NONE
;
1129 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_BUFFER_TOO_SMALL
,
1130 "Known-host write buffer too small");
1134 * libssh2_knownhost_writeline()
1136 * Ask libssh2 to convert a known host to an output line for storage.
1138 * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given
1139 * output buffer is too small to hold the desired output.
1142 libssh2_knownhost_writeline(LIBSSH2_KNOWNHOSTS
*hosts
,
1143 struct libssh2_knownhost
*known
,
1144 char *buffer
, size_t buflen
,
1145 size_t *outlen
, /* the amount of written data */
1148 struct known_host
*node
;
1150 if(known
->magic
!= KNOWNHOST_MAGIC
)
1151 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_INVAL
,
1152 "Invalid host information");
1156 return knownhost_writeline(hosts
, node
, buffer
, buflen
, outlen
, type
);
1160 * libssh2_knownhost_writefile()
1162 * Write hosts+key pairs to the given file.
1165 libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS
*hosts
,
1166 const char *filename
, int type
)
1168 struct known_host
*node
;
1170 int rc
= LIBSSH2_ERROR_NONE
;
1173 /* we only support this single file type for now, bail out on all other
1175 if(type
!= LIBSSH2_KNOWNHOST_FILE_OPENSSH
)
1176 return _libssh2_error(hosts
->session
,
1177 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED
,
1178 "Unsupported type of known-host information "
1181 file
= fopen(filename
, "w");
1183 return _libssh2_error(hosts
->session
, LIBSSH2_ERROR_FILE
,
1184 "Failed to open file");
1186 for(node
= _libssh2_list_first(&hosts
->head
);
1188 node
= _libssh2_list_next(&node
->node
)) {
1191 rc
= knownhost_writeline(hosts
, node
, buffer
, sizeof(buffer
), &wrote
,
1196 nwrote
= fwrite(buffer
, 1, wrote
, file
);
1197 if(nwrote
!= wrote
) {
1198 /* failed to write the whole thing, bail out */
1199 rc
= _libssh2_error(hosts
->session
, LIBSSH2_ERROR_FILE
,
1211 * libssh2_knownhost_get()
1213 * Traverse the internal list of known hosts. Pass NULL to 'prev' to get
1217 * 0 if a fine host was stored in 'store'
1219 * [negative] on errors
1222 libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS
*hosts
,
1223 struct libssh2_knownhost
**ext
,
1224 struct libssh2_knownhost
*oprev
)
1226 struct known_host
*node
;
1227 if(oprev
&& oprev
->node
) {
1228 /* we have a starting point */
1229 struct known_host
*prev
= oprev
->node
;
1231 /* get the next node in the list */
1232 node
= _libssh2_list_next(&prev
->node
);
1236 node
= _libssh2_list_first(&hosts
->head
);
1239 /* no (more) node */
1242 *ext
= knownhost_to_external(node
);