On Thu, 29 Nov 2007, Daniel Stenberg wrote:

Hi!

While I like the idea of being able to use the known_hosts file with this patch, it is far from clear to me how it is accomplished with this!

I attach my modified version of your patch to this mail, indented in libssh2-style and with the warnings fixed.

Since there's no prototypes in the public header file, how are these functions supposed to be used? (And note that public functions need to have LIBSSH2_API prefixes - I tried to fix this in my version of the patch) I would like to get some basic docs for them, and I'll happily assist to get them converted to man pages and get stored in the docs/ dir!
Index: src/Makefile.am
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/Makefile.am,v
retrieving revision 1.10
diff -u -r1.10 Makefile.am
--- src/Makefile.am     17 Jul 2007 13:22:55 -0000      1.10
+++ src/Makefile.am     3 Dec 2007 10:45:11 -0000
@@ -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
Index: src/libssh2_priv.h
===================================================================
RCS file: /cvsroot/libssh2/libssh2/src/libssh2_priv.h,v
retrieving revision 1.35
diff -u -r1.35 libssh2_priv.h
--- src/libssh2_priv.h  6 Aug 2007 20:48:06 -0000       1.35
+++ src/libssh2_priv.h  3 Dec 2007 10:45:11 -0000
@@ -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 */
+    unsigned 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 */
Index: src/sshentry.c
===================================================================
RCS file: src/sshentry.c
diff -N src/sshentry.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/sshentry.c      3 Dec 2007 10:45:11 -0000
@@ -0,0 +1,391 @@
+#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. */
+LIBSSH2_API 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;
+    /* workaround for the MD5 stuff */
+    libssh2_md5_ctx ctx;
+
+    /* 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, "%hu %hu ", &(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, (unsigned int *)&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;
+    }
+}
+
+LIBSSH2_API 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. */
+LIBSSH2_API int
+libssh2_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;
+}
-------------------------------------------------------------------------
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

Reply via email to