Hey
Here's my first public patch (numbered 3 since I made a few ones before
internally). I'm posting it here to attract your reviews and comments on the
general approach on the API etc. It is NOT complete, it has remaining bugs and
it leaks memory.
Some words on my changes:
* The old patch. I did not use the former patch and in fact I didn't even look
at it when I implemented it as I saw no point in doing that. I did read a
bunch of OpenSSH source code to figure out their file format though.
* linked list code. I got a bit tired of the fact that we don't have any
generic linked-list functions within libssh2 so I wrote up the first embryo
for one that I use for this new functionality. The plan would then be to
move all existing code that uses linked lists to use this new set.
* base64 encode. I had to add a base64 encoding function which was missing
in the code base so it helps to "bloat" my patch.
* The API is basically:
_init() - init a bundle of known hosts
_add() - add a known host
_del() - delete a known host
_free() - free an entire bundle of known hosts
_check() - check if a host+key is present in the bundle
And then there's the convenience function:
_parsefile() - reads a ~/.ssh/known_hosts file and add all entries to the
given bundle
Still missing in the API:
A) I think we also want a _writefile() or similar that can write a
known_hosts, or perhaps append to it...
B) there's currently no accessor to the bundle so there's no actual way for
an app to access the list of hosts. Any suggestions on how such a one
would work best?
* there's no docs other than some comments in the code/headers yet
* the patch includes changes to example/simple/ssh2_exec.c that makes use of
a few of these functions. Using that I've verified that the functions in
fact can verify my localhost's key agains my ~/.ssh/known_hosts file
* diffstat says:
example/simple/ssh2_exec.c | 42 ++-
include/libssh2.h | 117 ++++++++++
src/Makefile.am | 3
src/hostkey.c | 486 +++++++++++++++++++++++++++++++++++++++++++++
src/kex.c | 14 -
src/libssh2_priv.h | 38 +++
src/misc.c | 129 +++++++++++
src/misc.h | 61 +++++
8 files changed, 864 insertions(+), 26 deletions(-)
Fire away!
--
/ daniel.haxx.se
Index: example/simple/ssh2_exec.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/example/simple/ssh2_exec.c,v
retrieving revision 1.2
diff -u -r1.2 ssh2_exec.c
--- example/simple/ssh2_exec.c 5 May 2009 12:30:19 -0000 1.2
+++ example/simple/ssh2_exec.c 7 May 2009 06:47:24 -0000
@@ -26,7 +26,6 @@
#include <libssh2.h>
-
static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
struct timeval timeout;
@@ -59,11 +58,12 @@
int main(int argc, char *argv[])
{
+ const char *hostname = "127.0.0.1";
const char *commandline = "uptime";
const char *username = "user";
const char *password = "password";
unsigned long hostaddr;
- int sock, i;
+ int sock;
struct sockaddr_in sin;
const char *fingerprint;
LIBSSH2_SESSION *session;
@@ -71,16 +71,17 @@
int rc;
int exitcode;
int bytecount = 0;
+ size_t len;
+ LIBSSH2_KNOWNHOSTS *nh;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(2,0), &wsadata);
#endif
- if (argc > 1) {
- hostaddr = inet_addr(argv[1]);
- } else {
- hostaddr = htonl(0x7F000001);
- }
+ if (argc > 1)
+ /* must be ip address only */
+ hostname = argv[1];
+
if (argc > 2) {
username = argv[2];
}
@@ -91,6 +92,8 @@
commandline = argv[4];
}
+ hostaddr = inet_addr(hostname);
+
/* Ultra basic "connect to port 22 on localhost"
* Your code is responsible for creating the socket establishing the
* connection
@@ -141,17 +144,22 @@
return -1;
}
- /* At this point we havn't yet authenticated. The first thing to do
- * is check the hostkey's fingerprint against our known hosts Your app
- * may have it hard coded, may go to a file, may present it to the
- * user, that's your call
- */
- fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
- fprintf(stderr, "Fingerprint: ");
- for(i = 0; i < 16; i++) {
- fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
+ nh = libssh2_knownhost_init(session);
+
+ libssh2_knownhost_parsefile(nh, "/home/daniel/.ssh/known_hosts",
+ LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+
+ fingerprint = libssh2_session_hostkey(session, &len);
+ if(fingerprint) {
+ char *ptr;
+ int check;
+
+ check = libssh2_knownhost_check(nh, (char *)hostname,
+ (char *)fingerprint, len,
+ LIBSSH2_KNOWNHOST_TYPE_DEFAULT, &ptr);
+
+ fprintf(stderr, "Host check: %d\n", check);
}
- fprintf(stderr, "\n");
if ( strlen(password) != 0 ) {
/* We could authenticate via password */
Index: include/libssh2.h
===================================================================
RCS file: /cvsroot/libssh2/libssh2/include/libssh2.h,v
retrieving revision 1.98
diff -u -r1.98 libssh2.h
--- include/libssh2.h 2 Apr 2009 09:35:02 -0000 1.98
+++ include/libssh2.h 7 May 2009 06:47:24 -0000
@@ -240,6 +240,7 @@
typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION;
typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL;
typedef struct _LIBSSH2_LISTENER LIBSSH2_LISTENER;
+typedef struct _LIBSSH2_KNOWNHOSTS LIBSSH2_KNOWNHOSTS;
typedef struct _LIBSSH2_POLLFD {
unsigned char type; /* LIBSSH2_POLLFD_* below */
@@ -348,6 +349,7 @@
#define LIBSSH2_ERROR_INVALID_POLL_TYPE -35
#define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL -36
#define LIBSSH2_ERROR_EAGAIN -37
+#define LIBSSH2_ERROR_MEMORY -38
/* Session API */
LIBSSH2_API LIBSSH2_SESSION *
@@ -377,6 +379,9 @@
LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session,
int hash_type);
+LIBSSH2_API const char *libssh2_session_hostkey(LIBSSH2_SESSION *session,
+ size_t *len);
+
LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session,
int method_type,
const char *prefs);
@@ -664,6 +669,118 @@
const char *libssh2_version(int req_version_num);
+/*
+ * libssh2_knownhost_init
+ *
+ * Init a collection of known hosts. Returns the pointer to a collection.
+ *
+ */
+LIBSSH2_API LIBSSH2_KNOWNHOSTS *
+libssh2_knownhost_init(LIBSSH2_SESSION *session);
+
+/*
+ * libssh2_knownhost_add
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * The 'type' argument specifies on what format the given host is:
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ */
+
+#define LIBSSH2_KNOWNHOST_TYPE_DEFAULT (LIBSSH2_KNOWNHOST_TYPE_PLAIN | \
+ LIBSSH2_KNOWNHOST_KEY_RAW)
+
+/* host format */
+#define LIBSSH2_KNOWNHOST_TYPE_MASK 0xffff
+#define LIBSSH2_KNOWNHOST_TYPE_PLAIN 0
+#define LIBSSH2_KNOWNHOST_TYPE_SHA1 1 /* always base64 encoded */
+#define LIBSSH2_KNOWNHOST_TYPE_CUSTOM 2
+
+/* key format */
+#define LIBSSH2_KNOWNHOST_KEY_RAW 0
+#define LIBSSH2_KNOWNHOST_KEY_BASE64 (1<<16)
+
+/* type of key */
+#define LIBSSH2_KNOWNHOST_KEY_RSA1 (1<<17)
+#define LIBSSH2_KNOWNHOST_KEY_SSHRSA (2<<17)
+#define LIBSSH2_KNOWNHOST_KEY_SSHDSS (3<<17)
+
+LIBSSH2_API int
+libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts, char *host, char *salt,
+ char *key, size_t keylen, int typemask);
+
+/*
+ * libssh2_knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The type is the type/format of the given host name.
+ *
+ * plain - ascii "hostname.domain.tld"
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_* values, see below
+ *
+ */
+
+#define LIBSSH2_KNOWNHOST_CHECK_MATCH 0
+#define LIBSSH2_KNOWNHOST_CHECK_MISMATCH 1
+#define LIBSSH2_KNOWNHOST_CHECK_NOTFOUND 2
+#define LIBSSH2_KNOWNHOST_CHECK_FAILURE 3
+
+LIBSSH2_API int
+libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+ char *host, char *key, size_t keylen,
+ int typemask, char **keyp);
+
+/*
+ * libssh2_knownhost_del
+ *
+ * Remove a host from the collection of known hosts.
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts, char *host);
+
+/*
+ * libssh2_knownhost_free
+ *
+ * Free an entire collection of known hosts.
+ *
+ */
+LIBSSH2_API void
+libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts);
+
+/*
+ * libssh2_knownhost_parsefile
+ *
+ * Add hosts+key pairs from a given file.
+ *
+ * Returns a negative value for error or number of successfully added hosts.
+ *
+ * This implementation currently only knows one type, all others are reserved
+ * for future use.
+ */
+
+#define LIBSSH2_KNOWNHOST_FILE_OPENSSH 1
+
+LIBSSH2_API int
+libssh2_knownhost_parsefile(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *filename, int type);
+
/* NOTE NOTE NOTE
libssh2_trace() has no function in builds that aren't built with debug
enabled
Index: src/Makefile.am
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/Makefile.am,v
retrieving revision 1.19
diff -u -r1.19 Makefile.am
--- src/Makefile.am 26 Mar 2009 22:25:23 -0000 1.19
+++ src/Makefile.am 7 May 2009 06:47:24 -0000
@@ -3,7 +3,8 @@
libssh2_la_SOURCES = channel.c comp.c crypt.c hostkey.c kex.c mac.c misc.c \
packet.c publickey.c scp.c session.c sftp.c userauth.c libssh2_priv.h \
-openssl.h libgcrypt.h transport.c version.c transport.h channel.h comp.h mac.h
+openssl.h libgcrypt.h transport.c version.c transport.h channel.h comp.h mac.h \
+misc.h
if LIBGCRYPT
libssh2_la_SOURCES += libgcrypt.c pem.c
Index: src/hostkey.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/hostkey.c,v
retrieving revision 1.32
diff -u -r1.32 hostkey.c
--- src/hostkey.c 17 Mar 2009 13:48:35 -0000 1.32
+++ src/hostkey.c 7 May 2009 06:47:24 -0000
@@ -1,4 +1,5 @@
/* Copyright (c) 2004-2006, Sara Golemon <sa...@libssh2.org>
+ * Copyright (c) 2009 by Daniel Stenberg
* All rights reserved.
*
* Redistribution and use in source and binary forms,
@@ -35,7 +36,9 @@
* OF SUCH DAMAGE.
*/
+#include "libssh2.h"
#include "libssh2_priv.h"
+#include "misc.h"
/* Needed for struct iovec on some platforms */
#ifdef HAVE_SYS_UIO_H
@@ -453,3 +456,486 @@
return NULL;
}
}
+
+static void free_host(LIBSSH2_SESSION *session, struct known_host *entry)
+{
+ if(entry) {
+ if(entry->key)
+ LIBSSH2_FREE(session, entry->key);
+ if(entry->salt)
+ LIBSSH2_FREE(session, entry->salt);
+ if(entry->name)
+ LIBSSH2_FREE(session, entry->name);
+ LIBSSH2_FREE(session, entry);
+ }
+}
+
+/*
+ * libssh2_session_hostkey()
+ *
+ * Returns the server key and length.
+ *
+ */
+LIBSSH2_API const char *
+libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len)
+{
+ if(session->server_hostkey_len) {
+ if(len)
+ *len = session->server_hostkey_len;
+ return (char *) session->server_hostkey;
+ }
+ if(len)
+ *len = 0;
+ return NULL;
+}
+
+/*
+ * libssh2_knownhost_init
+ *
+ * Init a collection of known hosts. Returns the pointer to a collection.
+ *
+ */
+LIBSSH2_API LIBSSH2_KNOWNHOSTS *
+libssh2_knownhost_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_KNOWNHOSTS *knh =
+ LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS));
+
+ if(!knh)
+ return NULL;
+
+ knh->session = session;
+
+ _libssh2_list_init(&knh->head);
+
+ return knh;
+}
+
+/*
+ * libssh2_knownhost_add
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * The 'type' argument specifies on what format the given host and keys are:
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
+ char *host, char *salt,
+ char *key, size_t keylen,
+ int typemask)
+{
+ struct known_host *entry =
+ LIBSSH2_ALLOC(hosts->session, sizeof(struct known_host));
+ size_t hostlen = strlen(host);
+ int rc = LIBSSH2_ERROR_MEMORY;
+ char *ptr;
+ unsigned int ptrlen;
+
+ if(!entry)
+ return rc;
+
+ entry->type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
+
+ switch(entry->type) {
+ case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+ case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+ entry->name = LIBSSH2_ALLOC(hosts->session, hostlen+1);
+ if(!entry)
+ goto error;
+ memcpy(entry->name, host, hostlen+1);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+ rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+ host, hostlen);
+ if(rc)
+ goto error;
+ entry->name = ptr;
+ entry->name_len = ptrlen;
+
+ rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+ salt, strlen(salt));
+ if(rc)
+ goto error;
+ entry->salt = ptr;
+ entry->salt_len = ptrlen;
+ break;
+ default:
+ rc = LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if(typemask & LIBSSH2_KNOWNHOST_KEY_BASE64) {
+ /* the provided key is base64 encoded already */
+ if(!keylen)
+ keylen = strlen(key);
+ entry->key = LIBSSH2_ALLOC(hosts->session, keylen+1);
+ if(!entry)
+ goto error;
+ memcpy(entry->key, key, keylen+1);
+ }
+ else {
+ /* key is raw, we base64 encode it and store it as such */
+ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+ &ptr);
+ if(!nlen)
+ goto error;
+
+ entry->key = ptr;
+ }
+
+ /* add this new host to the big list of known hosts */
+ _libssh2_list_add(&hosts->head, &entry->node);
+
+ return LIBSSH2_ERROR_NONE;
+ error:
+ free_host(hosts->session, entry);
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+LIBSSH2_API int
+libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+ char *host, char *key, size_t keylen,
+ int typemask, char **keyp)
+{
+ struct known_host *node = _libssh2_list_first(&hosts->head);
+ struct known_host *badkey = NULL;
+ int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
+ char *keyalloc = NULL;
+ int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND;
+
+ if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1)
+ /* we can't work with a sha1 as given input */
+ return LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+
+ if(!(typemask & LIBSSH2_KNOWNHOST_KEY_BASE64)) {
+ /* we got a raw key input, convert it to base64 for the checks below */
+ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+ &keyalloc);
+ if(!nlen)
+ return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+
+ /* make the key point to this */
+ key = keyalloc;
+ keylen = nlen;
+ }
+
+ *keyp = NULL; /* no key found yet */
+ while (node) {
+ int match = 0;
+ switch(node->type) {
+ case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN)
+ match = !strcmp(host, node->name);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM)
+ match = !strcmp(host, node->name);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) {
+ /* when we have the sha1 version stored, we can use a plain
+ input to produce a hash to compare with the stored hash.
+
+ HMAC_Init(&mac_ctx, salt, len, md);
+ HMAC_Update(&mac_ctx, host, strlen(host));
+ HMAC_Final(&mac_ctx, result, NULL);
+ HMAC_cleanup(&mac_ctx);
+
+ */
+ libssh2_hmac_ctx ctx;
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ int i;
+
+ if(SHA_DIGEST_LENGTH != node->name_len) {
+ /* the name hash length must be the sha1 size or
+ we can't match it */
+ break;
+ }
+ libssh2_hmac_sha1_init(&ctx, node->salt, node->salt_len);
+ libssh2_hmac_update(ctx, (unsigned char *)host, strlen(host));
+ libssh2_hmac_final(ctx, hash);
+ libssh2_hmac_cleanup(&ctx);
+
+ if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH)) {
+ /* this is a node we're interested in */
+ match = 1;
+ }
+ }
+ break;
+ default: /* unsupported type */
+ break;
+ }
+ if(match) {
+ /* host name match, now compare the keys */
+ if(!strcmp(key, node->key)) {
+ /* they match! */
+ *keyp = node->key;
+ badkey = NULL;
+ rc = LIBSSH2_KNOWNHOST_CHECK_MATCH;
+ break;
+ }
+ else {
+ /* remember the first node that had a host match but a failed
+ key match since we continue our search from here */
+ if(!badkey)
+ badkey = node;
+ }
+ }
+ node= _libssh2_list_next(&node->node);
+ }
+
+ if(badkey) {
+ /* key mismatch */
+ *keyp = badkey->key;
+ rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+ }
+
+ if(keyalloc)
+ LIBSSH2_FREE(hosts->session, keyalloc);
+
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_del
+ *
+ * Remove a host from the collection of known hosts.
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts, char *host)
+{
+ return 0;
+}
+
+/*
+ * libssh2_knownhost_free
+ *
+ * Free an entire collection of known hosts.
+ *
+ */
+LIBSSH2_API void
+libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts)
+{
+
+}
+
+/*
+ * hostline()
+ *
+ * Parse a single known_host line pre-split into host and key.
+ *
+ * Note: this function assumes that the 'host' pointer points into a temporary
+ * buffer as it will write to it.
+ */
+static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ char *host, size_t hostlen,
+ char *key, size_t keylen)
+{
+ char *p;
+ char *salt = NULL;
+ int rc;
+ int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN;
+
+ /* Figure out host format */
+ if(strncmp(host, "|1|", 3))
+ /* old style plain text:
+
+ [name][,][ip-address] */
+ ;
+ else {
+ /* |1|[salt]|[hash] */
+ type = LIBSSH2_KNOWNHOST_TYPE_SHA1;
+
+ salt = &host[3]; /* skip the magic marker */
+
+ /* this is where the salt starts, find the end of it */
+ for(p = salt; *p && (*p != '|'); p++)
+ ;
+
+ if(*p=='|') {
+ char *hash = NULL;
+ *p=0; /* terminate the salt string */
+ hash = p+1; /* the hash is after the separator */
+
+ /* now make the host point to the hash */
+ hostlen = strlen(hash);
+ host = hash;
+ }
+ else
+ return 0;
+ }
+
+ if(keylen < 20)
+ return -1; /* TODO: better return code */
+
+ switch(key[0]) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ type |= LIBSSH2_KNOWNHOST_KEY_RSA1;
+
+ /* Note that the old-style keys (RSA1) aren't truly base64, but we
+ * claim it is for now since we can get away with strcmp()ing the
+ * entire anything anyway! We need to check and fix these to make them
+ * work properly.
+ */
+ break;
+
+ case 's': /* ssh-dss or ssh-rsa */
+ if(!strncmp(key, "ssh-dss", 7))
+ type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ else if(!strncmp(key, "ssh-rsa", 7))
+ type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ else
+ return -1; /* unknown */
+
+ key += 7;
+ keylen -= 7;
+
+ /* skip whitespaces */
+ while((*key ==' ') || (*key == '\t')) {
+ key++;
+ keylen--;
+ }
+ break;
+
+ default: /* unknown key format */
+ return -1;
+ }
+
+ rc = libssh2_knownhost_add(hosts, host, salt, key, keylen,
+ type | LIBSSH2_KNOWNHOST_KEY_BASE64);
+
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_parsefile
+ *
+ * Add hosts+key pairs from a given file.
+ *
+ * Returns a negative value for error or number of successfully added hosts.
+ *
+ * Line format:
+ *
+ * <host> <key>
+ *
+ * Where the two parts can be created like:
+ *
+ * <host> can be either
+ * <name> or <hash>
+ *
+ * <name> consists of
+ * [name,address] or just [name] or just [address]
+ *
+ * <hash> consists of
+ * |1|<salt>|hash
+ *
+ * <key> can be one of:
+ * [RSA bits] [e] [n as a decimal number]
+ * 'ssh-dss' [base64-encoded-key]
+ * 'ssh-rsa' [base64-encoded-key]
+ *
+ */
+
+#define LIBSSH2_KNOWNHOST_FILE_OPENSSH 1
+
+LIBSSH2_API int
+libssh2_knownhost_parsefile(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *filename, int type)
+{
+ FILE *file;
+ int num = 0;
+ char buf[2048];
+
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return -1;
+
+ file = fopen(filename, "r");
+ if(file) {
+ char *sep;
+ char *cp;
+ char *hostp;
+ char *key;
+ size_t hostlen;
+
+ while(fgets(buf, sizeof(buf), file)) {
+ cp = buf;
+
+ /* skip leading whitespaces */
+ while((*cp==' ') || (*cp == '\t'))
+ cp++;
+
+ if(!*cp || (*cp == '#') || (*cp == '\n'))
+ /* comment or empty line */
+ continue;
+
+ /* the host part starts here */
+ hostp = cp;
+
+ /* move over the host to the separator */
+ while(*cp && (*cp!=' ') && (*cp != '\t'))
+ cp++;
+
+ hostlen = cp - hostp;
+
+ *cp++ = 0; /* terminate the host string here */
+
+ /* the key starts after the whitespaces */
+ while(*cp && ((*cp==' ') || (*cp == '\t')))
+ cp++;
+
+ if(!*cp)
+ /* illegal line */
+ continue;
+
+ key = cp; /* the key starts here */
+
+ while(*cp && (*cp != '\n'))
+ cp++;
+
+ /* zero terminate where the newline is */
+ if(*cp == '\n')
+ *cp = 0;
+
+ /* deal with this one host+key line */
+ if(!hostline(hosts, hostp, hostlen, key, strlen(key)))
+ num++;
+ }
+ fclose(file);
+ }
+ else
+ return -1;
+ return num;
+}
Index: src/kex.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/kex.c,v
retrieving revision 1.45
diff -u -r1.45 kex.c
--- src/kex.c 27 Mar 2009 07:03:00 -0000 1.45
+++ src/kex.c 7 May 2009 06:47:25 -0000
@@ -158,7 +158,7 @@
if (exchange_state->state == libssh2_NB_state_sent) {
if (session->burn_optimistic_kexinit) {
- /* The first KEX packet to come along will be the guess initially
+ /* The first KEX packet to come along will be the guess initially
* sent by the server. That guess turned out to be wrong so we
* need to silently ignore it */
int burn_type;
@@ -445,12 +445,12 @@
ret = -1;
goto clean_exit;
}
- /* The first key exchange has been performed,
+ /* The first key exchange has been performed,
switch to active crypt/comp/mac mode */
session->state |= LIBSSH2_STATE_NEWKEYS;
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message");
- /* This will actually end up being just packet_type(1)
+ /* This will actually end up being just packet_type(1)
for this packet type anyway */
LIBSSH2_FREE(session, exchange_state->tmp);
@@ -641,10 +641,12 @@
exchange_state->k_value = NULL;
}
+#if 0
if (session->server_hostkey) {
LIBSSH2_FREE(session, session->server_hostkey);
session->server_hostkey = NULL;
}
+#endif
exchange_state->state = libssh2_NB_state_idle;
@@ -1013,7 +1015,7 @@
*/
static int kexinit(LIBSSH2_SESSION * session)
{
- /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) +
+ /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) +
reserved(4) + length longs(40) */
size_t data_len = 62;
size_t kex_len, hostkey_len = 0;
@@ -1069,8 +1071,8 @@
libssh2_random(s, 16);
s += 16;
- /* Ennumerating through these lists twice is probably (certainly?)
- inefficient from a CPU standpoint, but it saves multiple
+ /* Ennumerating through these lists twice is probably (certainly?)
+ inefficient from a CPU standpoint, but it saves multiple
malloc/realloc calls */
LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs,
libssh2_kex_methods);
Index: src/libssh2_priv.h
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/libssh2_priv.h,v
retrieving revision 1.56
diff -u -r1.56 libssh2_priv.h
--- src/libssh2_priv.h 27 Mar 2009 12:48:03 -0000 1.56
+++ src/libssh2_priv.h 7 May 2009 06:47:25 -0000
@@ -922,7 +922,43 @@
#define LIBSSH2_SOCKET_RECV_FLAGS(session) 0
#endif
-/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional methods via .so/.dll */
+/* -------- */
+
+/* First take towards a generic linked list handling code for libssh2
+ internals */
+
+struct list_head {
+ struct list_node *last;
+ struct list_node *first;
+};
+
+struct list_node {
+ struct list_node *next;
+ struct list_node *prev;
+};
+
+/* --------- */
+
+struct known_host {
+ struct list_node node;
+ char *name; /* points to the name or the hash (allocated) */
+ size_t name_len; /* needed for hashed data */
+ int type; /* plain, sha1, custom, ... */
+ char *salt; /* points to binary salt (allocated) */
+ size_t salt_len; /* size of salt */
+ char *key; /* the (allocated) associated key. This is kept base64
+ encoded in memory. */
+};
+
+struct _LIBSSH2_KNOWNHOSTS
+{
+ LIBSSH2_SESSION *session; /* the session this "belongs to" */
+ struct list_head head;
+};
+
+
+/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional
+ methods via .so/.dll */
struct _LIBSSH2_KEX_METHOD
{
Index: src/misc.c
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/misc.c,v
retrieving revision 1.29
diff -u -r1.29 misc.c
--- src/misc.c 27 Mar 2009 07:03:00 -0000 1.29
+++ src/misc.c 7 May 2009 06:47:25 -0000
@@ -37,6 +37,8 @@
*/
#include "libssh2_priv.h"
+#include "misc.h"
+
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -176,7 +178,7 @@
* Decode a base64 chunk and store it into a newly alloc'd buffer
*/
LIBSSH2_API int
-libssh2_base64_decode(LIBSSH2_SESSION * session, char **data,
+libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,
unsigned int *datalen, const char *src,
unsigned int src_len)
{
@@ -222,6 +224,86 @@
return 0;
}
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * _libssh2_base64_encode()
+ *
+ * Returns the length of the newly created base64 string. The third argument
+ * is a pointer to an allocated area holding the base64 data. If something
+ * went wrong, 0 is returned.
+ *
+ */
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+ const char *inp, size_t insize, char **outptr)
+{
+ unsigned char ibuf[3];
+ unsigned char obuf[4];
+ int i;
+ int inputparts;
+ char *output;
+ char *base64data;
+ const char *indata = inp;
+
+ *outptr = NULL; /* set to NULL in case of failure before we reach the end */
+
+ if(0 == insize)
+ insize = strlen(indata);
+
+ base64data = output = LIBSSH2_ALLOC(session, insize*4/3+4);
+ if(NULL == output)
+ return 0;
+
+ while(insize > 0) {
+ for (i = inputparts = 0; i < 3; i++) {
+ if(insize > 0) {
+ inputparts++;
+ ibuf[i] = *indata;
+ indata++;
+ insize--;
+ }
+ else
+ ibuf[i] = 0;
+ }
+
+ obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2);
+ obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+ ((ibuf[1] & 0xF0) >> 4));
+ obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+ ((ibuf[2] & 0xC0) >> 6));
+ obuf[3] = (unsigned char) (ibuf[2] & 0x3F);
+
+ switch(inputparts) {
+ case 1: /* only one byte read */
+ snprintf(output, 5, "%c%c==",
+ table64[obuf[0]],
+ table64[obuf[1]]);
+ break;
+ case 2: /* two bytes read */
+ snprintf(output, 5, "%c%c%c=",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]]);
+ break;
+ default:
+ snprintf(output, 5, "%c%c%c%c",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]],
+ table64[obuf[3]] );
+ break;
+ }
+ output += 4;
+ }
+ *output=0;
+ *outptr = base64data; /* make it return the actual data memory */
+
+ return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
#ifdef LIBSSH2DEBUG
LIBSSH2_API int
libssh2_trace(LIBSSH2_SESSION * session, int bitmask)
@@ -283,3 +365,48 @@
return 0;
}
#endif
+
+/* init the list head */
+void _libssh2_list_init(struct list_head *head)
+{
+ head->first = head->last = NULL;
+}
+
+/* add a node to the list */
+void _libssh2_list_add(struct list_head *head,
+ struct list_node *entry)
+{
+ /* we add this entry at the "top" so it has no next */
+ entry->next = NULL;
+
+ /* make our prev point to what the head thinks is next */
+ entry->prev = head->last;
+
+ /* and make head's last be us now */
+ head->last = entry;
+
+ /* make sure our 'prev' node points to us next */
+ if(entry->prev)
+ entry->prev->next = entry;
+ else
+ head->first = entry;
+}
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head)
+{
+ return head->first;
+}
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node)
+{
+ return node->next;
+}
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node)
+{
+ return node->prev;
+}
+
Index: src/misc.h
===================================================================
RCS file: src/misc.h
diff -N src/misc.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/misc.h 7 May 2009 06:47:25 -0000
@@ -0,0 +1,61 @@
+#ifndef __LIBSSH2_MISC_H
+#define __LIBSSH2_MISC_H
+/* Copyright (c) 2009 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include "libssh2_priv.h"
+
+void _libssh2_list_init(struct list_head *head);
+
+/* add a node to the list */
+void _libssh2_list_add(struct list_head *head,
+ struct list_node *entry);
+
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head);
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node);
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node);
+
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+ const char *inp, size_t insize, char **outptr);
+#endif /* _LIBSSH2_MISC_H */
------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
libssh2-devel mailing list
libssh2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libssh2-devel