Patch forwarded to the libssh2 list for Johnny Luong.
---------- Forwarded message ----------
Date: Wed, 28 Nov 2007 21:23:07 -0800
From: Johnny Luong <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
Subject: Re: patch for ssh user known_hosts
Hi Daniel,
Could you forward this to the libssh mailing list? I've tried sending a patch
to the list before and it doesn't quite work the way I thought it would. This
patch applies cleanly to both the 0.18 version and 0.17 version.
Best Regards,
Johnny Luong
--- ../../libssh2-0.17/src/libssh2_priv.h 2007-08-06 13:41:31.000000000
-0700
+++ libssh2_priv.h 2007-11-28 20:39:08.000000000 -0800
@@ -933,6 +933,30 @@
int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
};
+/**
+ * The format of this is described here:
+ * http://www.openbsd.org/cgi-bin/man.cgi?query=sshd&sektion=8
+ * and is parsed based on a mixture of the actual implementation
+ * as well as the man page itself.
+ */
+typedef struct ssh_knownhost_entry
+{
+ char *hostname_line;
+ /* NUL-terminated list of hostnames */
+ char **hostnames;
+ int hostnames_size;
+ /* pretty much always zero for ssh_rsa although there could be others */
+ int key_type;
+ unsigned short bits;
+ unsigned short exponent;
+ char *modulus;
+ int modulus_length;
+ /* version 1, version 2... */
+ int ssh_version;
+ /* points to sixteen bytes of checksum or nothing */
+ char *md5;
+} LIBSSH2_KNOWNHOSTS;
+
#define LIBSSH2_DBG_TRANS 1
#define LIBSSH2_DBG_KEX 2
#define LIBSSH2_DBG_AUTH 3
@@ -1141,4 +1165,32 @@
int _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
unsigned char **i, unsigned int *ilen);
+/* sshentry.c */
+/** @fn int libssh2_new_host_entry(LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS ** s, char *line)
+ * Allocates and parses a ssh host entry as provided by the
+ * NUL-terminated line.
+ * @param session An existing ssh session
+ * @param s the ssh_host_entry handle (of a ptr)
+ * @param line an ascii nul-terminated line containing the public key
+ * @return zero if successful, non-zero otherwise
+ */
+int libssh2_new_host_entry(LIBSSH2_SESSION *, LIBSSH2_KNOWNHOSTS **, char *);
+
+/** @fn void libssh2_free_host_entry(LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS *s)
+ * Deallocates any memory used by this struct.
+ * @param session An existing ssh session
+ * @param s the ssh_host_entry ptr
+ */
+void libssh2_free_host_entry(LIBSSH2_SESSION *, LIBSSH2_KNOWNHOSTS *);
+
+/** @fn int libssh2_host_entry_match(LIBSSH2_KNOWNHOSTS * x, char *host)
+ * Attempts to find out if the hostname provided matches one of the
+ * hostname entries in the LIBSSH2_KNOWNHOSTS.
+ * @param x the ssh_host_entry ptr
+ * @param host the hostname you are trying to match
+ * @return zero if match, non-zero otherwise
+ */
+int libssh2_host_entry_match(LIBSSH2_KNOWNHOSTS *, char *);
#endif /* LIBSSH2_H */
--- ../../libssh2-0.17/src/Makefile.am 2007-07-28 15:59:21.000000000 -0700
+++ Makefile.am 2007-11-28 21:03:08.000000000 -0800
@@ -3,7 +3,7 @@
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 pem.c transport.c
+libssh2_priv.h openssl.h libgcrypt.h pem.c transport.c sshentry.c
if LIBGCRYPT
libssh2_la_SOURCES += libgcrypt.c
--- ../../libssh2-0.17/src/sshentry.c 1969-12-31 16:00:00.000000000 -0800
+++ sshentry.c 2007-11-28 20:58:47.000000000 -0800
@@ -0,0 +1,393 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "libssh2_priv.h"
+
+static int
+ssh_host_parse_hostnames (LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS * s,
+ char *line,
+ char *end
+);
+
+static int
+ssh_host_parse_key (LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS * s,
+ char *line,
+ int is_base64_encoded
+);
+
+/* Returns zero if successful, > zero for malformed data, < 0 not supported. */
+int
+libssh2_new_host_entry(LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS ** s,
+ char *line
+)
+{
+ char *tmp = NULL;
+ LIBSSH2_KNOWNHOSTS *t = NULL;
+ int i;
+
+ if (line == NULL || *line == 0)
+ return 1;
+ if (s == NULL)
+ return 2;
+
+ tmp = strchr (line, ' ');
+ if (tmp == NULL)
+ return 3;
+
+
+ t = (LIBSSH2_KNOWNHOSTS *)
+ LIBSSH2_ALLOC(session, sizeof(LIBSSH2_KNOWNHOSTS));
+
+ t->hostname_line = NULL;
+ t->hostnames = NULL;
+ t->hostnames_size = t->bits = t->exponent = -1;
+ t->modulus = NULL;
+ t->modulus_length = -1;
+ t->ssh_version = -1;
+ t->md5 = NULL;
+
+ i = ssh_host_parse_hostnames (session, t, line, tmp);
+ if (i != 0) {
+ libssh2_free_host_entry (session, t);
+ return ((i > 0) ? 4 : -1);
+ }
+
+ line = tmp + 1;
+ tmp = strchr (line, ' ');
+ if (tmp != NULL)
+ tmp = strchr (tmp + 1, ' ');
+
+ i = ssh_host_parse_key (session, t, line, tmp == NULL ? 1 : 0);
+ if (i != 0) {
+ libssh2_free_host_entry (session, t);
+ return ((i > 0) ? 5 : -2);
+ }
+
+ *s = t;
+ return 0;
+}
+
+static int
+ssh_host_parse_hostnames (LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS * s,
+ char *line,
+ char *end
+)
+{
+ char *start;
+ char *comma = NULL;
+ int i;
+
+ /* TODO: we don't handle the hashed name format because the hashing
+ * mechanism isnt defined (at least based on the man page)
+ */
+ if (*line == '|')
+ return -1;
+ if (line == end || *line == ' ')
+ return 1;
+
+ s->hostname_line = (char *) LIBSSH2_ALLOC (session, (end - line) + 1);
+ strncpy (s->hostname_line, line, (end - line) + 1);
+ start = end = s->hostname_line + (end - line);
+ *end = 0;
+
+ s->hostnames_size = 1;
+ comma = s->hostname_line;
+ while ((comma = strchr (comma, ',')) != NULL) {
+ comma++;
+ if (*comma == ',' || *comma == 0) {
+ LIBSSH2_FREE (session, s->hostname_line);
+ s->hostname_line = NULL;
+ return 2;
+ }
+ s->hostnames_size++;
+ }
+ s->hostnames = (char **) LIBSSH2_ALLOC
+ (session, sizeof (char *) * s->hostnames_size);
+
+ start = comma = s->hostname_line;
+ i = 0;
+ while ((comma = strchr (comma, ',')) != NULL) {
+ *comma = 0;
+ s->hostnames[i] = start;
+
+ comma++;
+ start = comma;
+ i++;
+ }
+ s->hostnames[i] = start;
+
+ return 0;
+}
+
+/** Returns the number of bytes read or -1. */
+static int
+ssh_proto_str_read (LIBSSH2_SESSION * session,
+ char *line,
+ char **val,
+ char *end
+)
+{
+ unsigned int len;
+
+ if (line + 4 > end)
+ return -1;
+ len = (line[0] << 24) + (line[1] << 16) + (line[2] << 8) + line[3];
+ if (line + 4 + len > end)
+ return -1;
+
+ *val = LIBSSH2_ALLOC (session, len);
+ memcpy (*val, line + 4, len);
+ return len + 4;
+}
+static int
+ssh_host_parse_key (LIBSSH2_SESSION * session,
+ LIBSSH2_KNOWNHOSTS * s,
+ char *line,
+ int is_base64_encoded)
+{
+ int i, j;
+ char *tmp, *tmp2;
+ /*extern size_t Curl_base64_decode (const char *source,
+ unsigned char **outptr); */
+ char scratchdata[512];
+ /* workaround for the MD5 stuff */
+ libssh2_md5_ctx ctx;
+ /* void *ctx = (void *) scratchdata; */
+
+ /* the bits, exponent, modulus format */
+ if (is_base64_encoded == 0) {
+ s->ssh_version = 1;
+ s->key_type = 0;
+ if (!isdigit (*line))
+ return -1;
+ if (sscanf (line, "%hd %hd ", &(s->bits), &(s->exponent)) != 2)
+ return -2;
+ /* TODO:
+ * There's probably an acceptable range...
+ */
+ if (s->bits <= 0 || s->exponent <= 0)
+ return 1;
+
+ line = strchr (line, ' ');
+ if (line == NULL)
+ return -3;
+ line++;
+ line = strchr (line, ' ');
+ if (line == NULL)
+ return -4;
+ line++;
+ /* TODO:
+ * figure out what format modulus is in since its not clear
+ * from the man page
+ */
+ return -5;
+ }
+ else {
+ s->ssh_version = 2;
+ /* we only handle the rsa type */
+ if (strstr (line, "ssh-rsa") != line)
+ return -6;
+ s->key_type = 0;
+ line += 7;
+ if (*line != ' ')
+ return 2;
+ line++;
+ i = 0;
+ while (*line) {
+ if ((line[i] >= 0x30 && line[i] <= 0x39) ||
+ (line[i] >= 0x41 && line[i] <= 0x5a) ||
+ (line[i] >= 0x61 && line[i] <= 0x7a) ||
+ (line[i] == '+') || (line[i] == '/') || (line[i] == '='))
+ i++;
+ else
+ break;
+ }
+ if (i == 0)
+ return 3;
+ tmp = LIBSSH2_ALLOC (session, sizeof (char) * (i + 5));
+ strncpy (tmp, line, i);
+ /* this should hopefully avoid any issues with reading
+ * past the array if its malformed */
+ tmp[i] = tmp[i + 1] = tmp[i + 2] = tmp[i + 3] = tmp[i + 4] = 0;
+/*
+ i = j = Curl_base64_decode (tmp, (unsigned char **) &tmp2);
+*/
+
+/* FIXME:
+ * Must use a base64 decoder implementation in order to decode stuff.
+ */
+ {
+ /* TODO: rework the api interface instead of making a local instance */
+ i = libssh2_base64_decode(session, &tmp2, &j, tmp, strlen(tmp));
+ LIBSSH2_FREE(session, tmp);
+ if (i != 0)
+ return 4;
+
+ }
+
+ /* printf("Decode Size: %d\n", i); */
+ /* free (tmp); */
+
+
+#if LIBSSH2_MD5
+ s->md5 = LIBSSH2_ALLOC (session, 16);
+
+ libssh2_md5_init (&ctx);
+ libssh2_md5_update (ctx, tmp2, j);
+ libssh2_md5_final (ctx, s->md5);
+/*
+ MD5_Init (ctx);
+ MD5_Update (ctx, tmp2, j);
+ MD5_Final (s->md5, ctx);
+*/
+#endif
+
+
+ line = tmp2;
+ i = ssh_proto_str_read (session, line, &tmp, tmp2 + j);
+ if (i < 0) {
+ LIBSSH2_FREE (session, tmp2);
+ return 5;
+ }
+ /* TODO: verify that its ssh-rsa -- its the only one
+ * supported
+ */
+ if (!(i == 11 && tmp[0] == 's' && tmp[1] == 's' &&
+ tmp[2] == 'h' && tmp[3] == '-' && tmp[4] == 'r' &&
+ tmp[5] == 's' && tmp[6] == 'a')) {
+ free (tmp);
+ free (tmp2);
+ return 8;
+ }
+
+ LIBSSH2_FREE (session, tmp);
+ line += i;
+ i = ssh_proto_str_read (session, line, &tmp, tmp2 + j);
+ if (i < 0) {
+ LIBSSH2_FREE (session, tmp2);
+ return 6;
+ }
+ /* TODO: verify that the exponent is valid */
+ if (i == 5)
+ s->exponent = (unsigned short) ((unsigned char) *tmp);
+ else {
+ LIBSSH2_FREE (session, tmp);
+ LIBSSH2_FREE (session, tmp2);
+ return 9;
+ }
+
+ LIBSSH2_FREE (session, tmp);
+ line += i;
+ i = ssh_proto_str_read (session, line, &tmp, tmp2 + j);
+ if (i < 0) {
+ LIBSSH2_FREE (session, tmp2);
+ return 7;
+ }
+
+ /* TODO: the modulus may need to be converted to
+ * big integer format
+ */
+ s->modulus_length = i - 4;
+ s->modulus = tmp;
+
+ s->bits = (s->modulus_length - 1) * 8;
+
+ LIBSSH2_FREE (session, tmp2);
+ return 0;
+ }
+}
+void
+libssh2_free_host_entry(LIBSSH2_SESSION * session, LIBSSH2_KNOWNHOSTS * s)
+{
+ /* int i; */
+ if (s == NULL)
+ return;
+
+ if (s->hostname_line != NULL) {
+ LIBSSH2_FREE (session, s->hostname_line);
+ s->hostname_line = NULL;
+ }
+/*
+ for (i=0; i < s->hostnames_size; i++) {
+ if (s->hostnames[i] != NULL) {
+ free(s->hostnames[i]);
+ s->hostnames[i] = NULL;
+ }
+ }
+*/
+ if (s->hostnames != NULL && s->hostnames_size > 0) {
+ LIBSSH2_FREE (session, s->hostnames);
+ s->hostnames = NULL;
+ }
+ s->hostnames_size = s->bits = s->exponent = -1;
+
+ if (s->modulus != NULL) {
+ LIBSSH2_FREE (session, s->modulus);
+ s->modulus = NULL;
+ }
+ s->modulus_length = -1;
+ s->ssh_version = -1;
+
+ if (s->md5 != NULL) {
+ LIBSSH2_FREE (session, s->md5);
+ s->md5 = NULL;
+ }
+
+ LIBSSH2_FREE (session, s);
+}
+
+#ifdef SSH_HOSTNAME_TESTS
+int
+ssh_unit_tests (int argc, char **argv)
+{
+ char *l[] = {
+ "closenet,...,192.0.2.53 1024 37 159...93 closenet.example.net",
+ "cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....=",
+ " cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....=",
+ "",
+ ",",
+ "f, ",
+ "cvs.example.net ssh-rsa AAAA1234.....=",
+ "192.168.30.118 ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAQEAwWVqxKm2Biwilakq9Ex8/tzHVQjRrzEkwlrWTDneptodVgqAzXUFQSa6Oj9AwzdDPhKe71vTv7RhXYg0ZvB1a5dIkzgCdoF/mIuTb80LvK7f0NxCaAHWODuHbwlJeMmjHV0WFsjsdOf690fPqeinD/8jfBQB950M1K3Qesib9H75gsnawF06MzZ52nC1HHi8mG2tGy2PMyP+mJs7KN1v4T+nobZ10ePe1dMqYXMdro/PB0JQmuGL7bBR5GRDEkK6nFcp2HsvuzXSeWZJcmWDdo+1n0cNg2th5VEIxrrFG5iy0CA2AXVPMqkf3VrAXGXV66dJTGtBqZ5GoxJCxDgW6w==",
+ "|1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM=
ssh-rsaAAAA1234.....="
+ };
+ int s;
+ int cases = sizeof (l) / sizeof (char *);
+
+ if (argc == 2) {
+ s = atoi (argv[1]);
+ if (s >= 0 && s < cases) {
+ LIBSSH2_KNOWNHOSTS *x = NULL;
+ printf ("%d\n", s = new_ssh_host_entry (&x, l[s]));
+ libssh2_free_host_entry (x);
+ return s;
+ }
+ }
+}
+#endif
+/** Returns 0 for a match, non-zero otherwise. */
+int
+ssh_host_entry_match (LIBSSH2_KNOWNHOSTS * x, char *host)
+{
+ /* TODO: Add pattern matching and/or DNS matching against
+ * to entries found in x
+ */
+ int i;
+ if (host == NULL || x == NULL)
+ return -1;
+
+ /* FIXME: this should use a case-insensitive compare as dns hostnames
+ * are generally case insensitive anyways
+ */
+ for (i = 0; i < x->hostnames_size; i++)
+ if (!strcmp (x->hostnames[i], host))
+ return 0;
+
+ return 1;
+}
--- ../../libssh2-0.17/src/Makefile.in 2007-07-31 03:02:52.000000000 -0700
+++ Makefile.in 2007-11-28 21:02:50.000000000 -0800
@@ -65,12 +65,12 @@
am__libssh2_la_SOURCES_DIST = 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 pem.c \
- transport.c libgcrypt.c openssl.c
+ transport.c libgcrypt.c openssl.c sshentry.c
@[EMAIL PROTECTED] = libgcrypt.lo
@[EMAIL PROTECTED] = openssl.lo
am_libssh2_la_OBJECTS = channel.lo comp.lo crypt.lo hostkey.lo kex.lo \
mac.lo misc.lo packet.lo publickey.lo scp.lo session.lo \
- sftp.lo userauth.lo pem.lo transport.lo $(am__objects_1) \
+ sftp.lo userauth.lo pem.lo transport.lo sshentry.lo $(am__objects_1) \
$(am__objects_2)
libssh2_la_OBJECTS = $(am_libssh2_la_OBJECTS)
DEFAULT_INCLUDES =
-------------------------------------------------------------------------
SF.Net email is sponsored by: The Future of Linux Business White Paper
from Novell. From the desktop to the data center, Linux is going
mainstream. Let it simplify your IT future.
http://altfarm.mediaplex.com/ad/ck/8857-50307-18918-4
_______________________________________________
libssh2-devel mailing list
libssh2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libssh2-devel