From 942a40b48250c92eeb0cf620ded6e943a4de025a Mon Sep 17 00:00:00 2001
From: Alexander Lamaison <awl03@doc.ic.ac.uk>
Date: Tue, 23 Feb 2010 21:55:26 +0000
Subject: [PATCH] Call libssh2_error for every knownhost API failure.

The libssh2 API calls should set the last error code and a message when
returning a failure by calling libssh2_error.  This changeset adds these
calls to the libssh2_knownhost_* API as well as libssh2_base64_decode.

This change also makes libssh2_error into a function rather than a macro.
Its implementation is moved to misc.c.  This function returns the error
code passed to it allowing callers to return the error value directly
without duplicating the error code.
---
 src/knownhost.c    |  168 +++++++++++++++++++++++++++++++++++++--------------
 src/libssh2_priv.h |   30 +---------
 src/misc.c         |   40 ++++++++++++
 3 files changed, 164 insertions(+), 74 deletions(-)

diff --git a/src/knownhost.c b/src/knownhost.c
index 64c008d..3110289 100644
--- a/src/knownhost.c
+++ b/src/knownhost.c
@@ -84,8 +84,12 @@ libssh2_knownhost_init(LIBSSH2_SESSION *session)
     LIBSSH2_KNOWNHOSTS *knh =
         LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS));
 
-    if(!knh)
+    if(!knh) {
+        libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                      "Unable to allocate memory for known-hosts "
+                      "collection", 0);
         return NULL;
+    }
 
     knh->session = session;
 
@@ -144,16 +148,21 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
     struct known_host *entry =
         LIBSSH2_ALLOC(hosts->session, sizeof(struct known_host));
     size_t hostlen = strlen(host);
-    int rc = LIBSSH2_ERROR_ALLOC;
+    int rc;
     char *ptr;
     unsigned int ptrlen;
 
-    if(!entry)
-        return rc;
+    if(!entry) {
+        return libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                             "Unable to allocate memory for known host "
+                             "entry", 0);
+    }
 
-    if(!(typemask & LIBSSH2_KNOWNHOST_KEY_MASK))
+    if(!(typemask & LIBSSH2_KNOWNHOST_KEY_MASK)) {
         /* make sure we have a key type set */
-        return LIBSSH2_ERROR_INVAL;
+        return libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+                             "No key type set", 0);
+    }
 
     memset(entry, 0, sizeof(struct known_host));
 
@@ -163,8 +172,11 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
     case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
     case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
         entry->name = LIBSSH2_ALLOC(hosts->session, hostlen+1);
-        if(!entry->name)
+        if(!entry->name) {
+            rc = libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                               "Unable to allocate memory for host name", 0);
             goto error;
+        }
         memcpy(entry->name, host, hostlen+1);
         break;
     case LIBSSH2_KNOWNHOST_TYPE_SHA1:
@@ -183,7 +195,8 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
         entry->salt_len = ptrlen;
         break;
     default:
-        rc = LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+        rc = libssh2_error(hosts->session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                           "Unknown host name type", 0);
         goto error;
     }
 
@@ -192,8 +205,11 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
         if(!keylen)
             keylen = strlen(key);
         entry->key = LIBSSH2_ALLOC(hosts->session, keylen+1);
-        if(!entry->key)
+        if(!entry->key) {
+            rc = libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                               "Unable to allocate memory for key", 0);
             goto error;
+        }
         memcpy(entry->key, key, keylen+1);
         entry->key[keylen]=0; /* force a terminating zero trailer */
     }
@@ -201,8 +217,12 @@ libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
         /* 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)
+        if(!nlen) {
+            rc = libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                               "Unable to allocate memory for "
+                               "base64-encoded key", 0);
             goto error;
+        }
 
         entry->key = ptr;
     }
@@ -257,8 +277,12 @@ libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
         /* 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)
+        if(!nlen) {
+            libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                          "Unable to allocate memory for base64-encoded "
+                          "key", 0);
             return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+        }
 
         /* make the key point to this */
         key = keyalloc;
@@ -344,9 +368,12 @@ libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts,
                       struct libssh2_knownhost *entry)
 {
     struct known_host *node;
-    if(!entry || (entry->magic != KNOWNHOST_MAGIC))
-        /* check that this was retrieved the right way or get out */
-        return LIBSSH2_ERROR_INVAL;
+    
+    /* check that this was retrieved the right way or get out */
+    if(!entry || (entry->magic != KNOWNHOST_MAGIC)) {
+        return libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+                             "Invalid host information", 0);
+    }
 
     /* get the internal node pointer */
     node = entry->node;
@@ -440,8 +467,12 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
         if(*p=='|') {
             const char *hash = NULL;
             size_t saltlen = p - salt;
-            if(saltlen >= (sizeof(saltbuf)-1))
-                return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED; /* weird length */
+            if(saltlen >= (sizeof(saltbuf)-1)) { /* weird length */
+                return libssh2_error(hosts->session,
+                                     LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                                     "Failed to parse known_hosts line "
+                                     "(unexpectedly long salt)", 0);
+            }
 
             memcpy(saltbuf, salt, saltlen);
             saltbuf[saltlen] = 0; /* zero terminate */
@@ -457,11 +488,15 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
             return 0;
     }
 
-    /* make some checks that the lenghts seem sensible */
+    /* make some checks that the lengths seem sensible */
     if((keylen < 20) ||
        (seplen >= sizeof(hostbuf)-1) ||
-       (hostlen >= sizeof(hostbuf)-1))
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+       (hostlen >= sizeof(hostbuf)-1)) {
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Failed to parse known_hosts line "
+                             "(unexpected length)", 0);
+    }
 
     switch(key[0]) {
     case '0': case '1': case '2': case '3': case '4':
@@ -480,8 +515,12 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
             type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
         else if(!strncmp(key, "ssh-rsa", 7))
             type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
-        else
-            return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED; /* unknown key type */
+        else {
+            /* unknown key type */
+            return libssh2_error(hosts->session,
+                                 LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                                 "Unknown key type", 0);
+        }
 
         key += 7;
         keylen -= 7;
@@ -494,7 +533,9 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
         break;
 
     default: /* unknown key format */
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Unknown key format", 0);
     }
 
     if(sep) {
@@ -560,8 +601,12 @@ libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS *hosts,
     size_t keylen;
     int rc;
 
-    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) {
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Unsupported type of known-host information "
+                             "store", 0);
+    }
 
     cp = line;
 
@@ -593,8 +638,11 @@ libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS *hosts,
     }
 
     if(!*cp || !len)
-        /* illegal line */
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+        /* illegal line */ {
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Failed to parse known_hosts line", 0);
+    }
 
     keyp = cp; /* the key starts here */
     keylen = len;
@@ -634,8 +682,12 @@ libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts,
     int num = 0;
     char buf[2048];
 
-    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) {
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Unsupported type of known-host information "
+                             "store", 0);
+    }
 
     file = fopen(filename, "r");
     if(file) {
@@ -646,8 +698,10 @@ libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts,
         }
         fclose(file);
     }
-    else
-        return LIBSSH2_ERROR_FILE;
+    else {
+        return libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+                             "Failed to open file", 0);
+    }
     return num;
 }
 
@@ -681,8 +735,12 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
 
     /* we only support this single file type for now, bail out on all other
        attempts */
-    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) {
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Unsupported type of known-host information "
+                             "store", 0);
+    }
 
     tindex = (node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) >>
         LIBSSH2_KNOWNHOST_KEY_SHIFT;
@@ -696,15 +754,20 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
         char *saltalloc;
         nlen = _libssh2_base64_encode(hosts->session, node->name,
                                       node->name_len, &namealloc);
-        if(!nlen)
-            return LIBSSH2_ERROR_ALLOC;
+        if(!nlen) {
+            return libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                                 "Unable to allocate memory for "
+                                 "base64-encoded host name", 0);
+        }
 
         nlen = _libssh2_base64_encode(hosts->session,
                                       node->salt, node->salt_len,
                                       &saltalloc);
         if(!nlen) {
             free(namealloc);
-            return LIBSSH2_ERROR_ALLOC;
+            return libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+                                 "Unable to allocate memory for "
+                                 "base64-encoded salt", 0);
         }
 
         nlen = strlen(saltalloc) + strlen(namealloc) + strlen(keytype) +
@@ -713,8 +776,10 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
         if(nlen <= buflen)
             sprintf(buf, "|1|%s|%s%s %s\n", saltalloc, namealloc, keytype,
                     node->key);
-        else
-            rc = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+        else {
+            rc = libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+                               "Known-host write buffer too small", 0);
+        }
 
         free(namealloc);
         free(saltalloc);
@@ -725,8 +790,10 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
         if(nlen <= buflen)
             /* these types have the plain name */
             sprintf(buf, "%s%s %s\n", node->name, keytype, node->key);
-        else
-            rc = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+        else {
+            rc = libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+                               "Known-host write buffer too small", 0);
+        }
     }
 
     /* we report the full length of the data with the trailing zero excluded */
@@ -752,8 +819,10 @@ libssh2_knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
 {
     struct known_host *node;
 
-    if(known->magic != KNOWNHOST_MAGIC)
-        return LIBSSH2_ERROR_INVAL;
+    if(known->magic != KNOWNHOST_MAGIC) {
+        return libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+                             "Invalid host information", 0);
+    }
 
     node = known->node;
 
@@ -776,12 +845,18 @@ libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts,
 
     /* we only support this single file type for now, bail out on all other
        attempts */
-    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
-        return LIBSSH2_ERROR_METHOD_NOT_SUPPORTED;
+    if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH) {
+        return libssh2_error(hosts->session,
+                             LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+                             "Unsupported type of known-host information "
+                             "store", 0);
+    }
 
     file = fopen(filename, "w");
-    if(!file)
-        return LIBSSH2_ERROR_FILE;
+    if(!file) {
+        return libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+                             "Failed to open file", 0);
+    }
 
     for(node = _libssh2_list_first(&hosts->head);
         node;
@@ -796,7 +871,8 @@ libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts,
         nwrote = fwrite(buffer, 1, wrote, file);
         if(nwrote != wrote) {
             /* failed to write the whole thing, bail out */
-            rc = LIBSSH2_ERROR_FILE;
+            rc = libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+                               "Write failed", 0);
             break;
         }
     }
diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
index 3d03807..aa0c435 100644
--- a/src/libssh2_priv.h
+++ b/src/libssh2_priv.h
@@ -1036,34 +1036,8 @@ _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
 #endif
 #endif
 
-#ifdef LIBSSH2DEBUG
-#define libssh2_error(session, errcode, errmsg, should_free)    \
-{ \
-    if (session->err_msg && session->err_should_free) { \
-        LIBSSH2_FREE(session, session->err_msg); \
-    } \
-    session->err_msg = (char *)errmsg; \
-    session->err_msglen = strlen(errmsg); \
-    session->err_should_free = should_free; \
-    session->err_code = errcode; \
-    _libssh2_debug(session, LIBSSH2_TRACE_ERROR, "%d - %s", session->err_code, session->err_msg); \
-}
-
-#else /* ! LIBSSH2DEBUG */
-
-#define libssh2_error(session, errcode, errmsg, should_free)    \
-{ \
-    if (session->err_msg && session->err_should_free) { \
-        LIBSSH2_FREE(session, session->err_msg); \
-    } \
-    session->err_msg = (char *)errmsg; \
-    session->err_msglen = strlen(errmsg); \
-    session->err_should_free = should_free; \
-    session->err_code = errcode; \
-}
-
-#endif /* ! LIBSSH2DEBUG */
-
+int libssh2_error(LIBSSH2_SESSION* session, int errcode, char* errmsg,
+                  int should_free);
 
 #define LIBSSH2_SOCKET_UNKNOWN                   1
 #define LIBSSH2_SOCKET_CONNECTED                 0
diff --git a/src/misc.c b/src/misc.c
index 44e68e1..9d5b4f1 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -49,6 +49,42 @@
 
 #include <errno.h>
 
+#ifdef LIBSSH2DEBUG
+
+int libssh2_error(LIBSSH2_SESSION* session, int errcode, char* errmsg,
+                  int should_free)
+{
+    if (session->err_msg && session->err_should_free) {
+        LIBSSH2_FREE(session, session->err_msg);
+    }
+    session->err_msg = errmsg;
+    session->err_msglen = strlen(errmsg);
+    session->err_should_free = should_free;
+    session->err_code = errcode;
+    _libssh2_debug(session, LIBSSH2_TRACE_ERROR, "%d - %s", session->err_code,
+                   session->err_msg);
+
+    return errcode;
+}
+
+#else /* ! LIBSSH2DEBUG */
+
+int libssh2_error(LIBSSH2_SESSION* session, int errcode, char* errmsg,
+                  int should_free)
+{
+    if (session->err_msg && session->err_should_free) {
+        LIBSSH2_FREE(session, session->err_msg);
+    }
+    session->err_msg = errmsg;
+    session->err_msglen = strlen(errmsg);
+    session->err_should_free = should_free;
+    session->err_code = errcode;
+
+    return errcode;
+}
+
+#endif /* ! LIBSSH2DEBUG */
+
 #ifdef WIN32
 static int wsa2errno(void)
 {
@@ -189,6 +225,8 @@ libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,
     *data = LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1);
     d = (unsigned char *) *data;
     if (!d) {
+        libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                      "Unable to allocate memory for base64 decoding", 0);
         return -1;
     }
 
@@ -217,6 +255,8 @@ libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,
         /* Invalid -- We have a byte which belongs exclusively to a partial
            octet */
         LIBSSH2_FREE(session, *data);
+        libssh2_error(session, LIBSSH2_ERROR_INVAL,
+                      "Invalid data (byte belonging to partial octet)", 0);
         return -1;
     }
 
-- 
1.6.5.1.1367.gcd48

