Unify GET_32BIT()/PUT_32BIT() et al from numerous source files into misc.h.
[u/mdw/putty] / import.c
index 01e2628..25012d2 100644 (file)
--- a/import.c
+++ b/import.c
 #include "ssh.h"
 #include "misc.h"
 
-#define PUT_32BIT(cp, value) do { \
-  (cp)[3] = (unsigned char)(value); \
-  (cp)[2] = (unsigned char)((value) >> 8); \
-  (cp)[1] = (unsigned char)((value) >> 16); \
-  (cp)[0] = (unsigned char)((value) >> 24); } while (0)
-
-#define GET_32BIT(cp) \
-    (((unsigned long)(unsigned char)(cp)[0] << 24) | \
-    ((unsigned long)(unsigned char)(cp)[1] << 16) | \
-    ((unsigned long)(unsigned char)(cp)[2] << 8) | \
-    ((unsigned long)(unsigned char)(cp)[3]))
-
 int openssh_encrypted(const Filename *filename);
-struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase);
+struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
+                                 const char **errmsg_p);
 int openssh_write(const Filename *filename, struct ssh2_userkey *key,
                  char *passphrase);
 
 int sshcom_encrypted(const Filename *filename, char **comment);
-struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase);
+struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
+                                const char **errmsg_p);
 int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
                 char *passphrase);
 
@@ -54,7 +44,7 @@ int import_possible(int type)
 int import_target_type(int type)
 {
     /*
-     * There are no known foreign SSH1 key formats.
+     * There are no known foreign SSH-1 key formats.
      */
     return SSH_KEYTYPE_SSH2;
 }
@@ -76,29 +66,29 @@ int import_encrypted(const Filename *filename, int type, char **comment)
 }
 
 /*
- * Import an SSH1 key.
+ * Import an SSH-1 key.
  */
 int import_ssh1(const Filename *filename, int type,
-               struct RSAKey *key, char *passphrase)
+               struct RSAKey *key, char *passphrase, const char **errmsg_p)
 {
     return 0;
 }
 
 /*
- * Import an SSH2 key.
+ * Import an SSH-2 key.
  */
 struct ssh2_userkey *import_ssh2(const Filename *filename, int type,
-                                char *passphrase)
+                                char *passphrase, const char **errmsg_p)
 {
     if (type == SSH_KEYTYPE_OPENSSH)
-       return openssh_read(filename, passphrase);
+       return openssh_read(filename, passphrase, errmsg_p);
     if (type == SSH_KEYTYPE_SSHCOM)
-       return sshcom_read(filename, passphrase);
+       return sshcom_read(filename, passphrase, errmsg_p);
     return NULL;
 }
 
 /*
- * Export an SSH1 key.
+ * Export an SSH-1 key.
  */
 int export_ssh1(const Filename *filename, int type, struct RSAKey *key,
                char *passphrase)
@@ -107,7 +97,7 @@ int export_ssh1(const Filename *filename, int type, struct RSAKey *key,
 }
 
 /*
- * Export an SSH2 key.
+ * Export an SSH-2 key.
  */
 int export_ssh2(const Filename *filename, int type,
                 struct ssh2_userkey *key, char *passphrase)
@@ -315,7 +305,8 @@ struct openssh_key {
     int keyblob_len, keyblob_size;
 };
 
-static struct openssh_key *load_openssh_key(const Filename *filename)
+static struct openssh_key *load_openssh_key(const Filename *filename,
+                                           const char **errmsg_p)
 {
     struct openssh_key *ret;
     FILE *fp;
@@ -333,13 +324,13 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
 
     fp = f_open(*filename, "r");
     if (!fp) {
-       errmsg = "Unable to open key file";
+       errmsg = "unable to open key file";
        goto error;
     }
     if (!fgets(buffer, sizeof(buffer), fp) ||
        0 != strncmp(buffer, "-----BEGIN ", 11) ||
        0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) {
-       errmsg = "File does not begin with OpenSSH key header";
+       errmsg = "file does not begin with OpenSSH key header";
        goto error;
     }
     if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n"))
@@ -347,14 +338,14 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
     else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n"))
        ret->type = OSSH_DSA;
     else {
-       errmsg = "Unrecognised key type";
+       errmsg = "unrecognised key type";
        goto error;
     }
 
     headers_done = 0;
     while (1) {
        if (!fgets(buffer, sizeof(buffer), fp)) {
-           errmsg = "Unexpected end of file";
+           errmsg = "unexpected end of file";
            goto error;
        }
        if (0 == strncmp(buffer, "-----END ", 9) &&
@@ -362,7 +353,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
            break;                     /* done */
        if ((p = strchr(buffer, ':')) != NULL) {
            if (headers_done) {
-               errmsg = "Header found in body of key data";
+               errmsg = "header found in body of key data";
                goto error;
            }
            *p++ = '\0';
@@ -379,7 +370,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
                int i, j;
 
                if (strncmp(p, "DES-EDE3-CBC,", 13)) {
-                   errmsg = "Ciphers other than DES-EDE3-CBC not supported";
+                   errmsg = "ciphers other than DES-EDE3-CBC not supported";
                    goto error;
                }
                p += 13;
@@ -390,7 +381,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
                    p += 2;
                }
                if (i < 8) {
-                   errmsg = "Expected 16-digit iv in DEK-Info";
+                   errmsg = "expected 16-digit iv in DEK-Info";
                    goto error;
                }
            }
@@ -409,7 +400,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
                     len = base64_decode_atom(base64_bit, out);
 
                     if (len <= 0) {
-                        errmsg = "Invalid base64 encoding";
+                        errmsg = "invalid base64 encoding";
                         goto error;
                     }
 
@@ -431,17 +422,18 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
     }
 
     if (ret->keyblob_len == 0 || !ret->keyblob) {
-       errmsg = "Key body not present";
+       errmsg = "key body not present";
        goto error;
     }
 
     if (ret->encrypted && ret->keyblob_len % 8 != 0) {
-       errmsg = "Encrypted key blob is not a multiple of cipher block size";
+       errmsg = "encrypted key blob is not a multiple of cipher block size";
        goto error;
     }
 
     memset(buffer, 0, sizeof(buffer));
     memset(base64_bit, 0, sizeof(base64_bit));
+    if (errmsg_p) *errmsg_p = NULL;
     return ret;
 
     error:
@@ -455,12 +447,13 @@ static struct openssh_key *load_openssh_key(const Filename *filename)
         memset(&ret, 0, sizeof(ret));
        sfree(ret);
     }
+    if (errmsg_p) *errmsg_p = errmsg;
     return NULL;
 }
 
 int openssh_encrypted(const Filename *filename)
 {
-    struct openssh_key *key = load_openssh_key(filename);
+    struct openssh_key *key = load_openssh_key(filename, NULL);
     int ret;
 
     if (!key)
@@ -473,9 +466,10 @@ int openssh_encrypted(const Filename *filename)
     return ret;
 }
 
-struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
+struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase,
+                                 const char **errmsg_p)
 {
-    struct openssh_key *key = load_openssh_key(filename);
+    struct openssh_key *key = load_openssh_key(filename, errmsg_p);
     struct ssh2_userkey *retkey;
     unsigned char *p;
     int ret, id, len, flags;
@@ -483,9 +477,9 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
     struct ssh2_userkey *retval = NULL;
     char *errmsg;
     unsigned char *blob;
-    int blobsize, blobptr, privptr;
-    char *modptr;
-    int modlen;
+    int blobsize = 0, blobptr, privptr;
+    char *modptr = NULL;
+    int modlen = 0;
 
     blob = NULL;
 
@@ -559,6 +553,8 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
        num_integers = 9;
     else if (key->type == OSSH_DSA)
        num_integers = 6;
+    else
+       num_integers = 0;              /* placate compiler warnings */
 
     /*
      * Space to create key blob in.
@@ -580,6 +576,7 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
        if (ret < 0 || id != 2 ||
            key->keyblob+key->keyblob_len-p < len) {
            errmsg = "ASN.1 decoding failure";
+           retval = SSH2_WRONG_PASSPHRASE;
            goto error;
        }
 
@@ -589,7 +586,7 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
             * this is some sort of version indication).
             */
            if (len != 1 || p[0] != 0) {
-               errmsg = "Version number mismatch";
+               errmsg = "version number mismatch";
                goto error;
            }
        } else if (key->type == OSSH_RSA) {
@@ -659,6 +656,7 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
     sfree(key->keyblob);
     memset(&key, 0, sizeof(key));
     sfree(key);
+    if (errmsg_p) *errmsg_p = errmsg;
     return retval;
 }
 
@@ -666,7 +664,7 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
                  char *passphrase)
 {
     unsigned char *pubblob, *privblob, *spareblob;
-    int publen, privlen, sparelen;
+    int publen, privlen, sparelen = 0;
     unsigned char *outblob;
     int outlen;
     struct mpint_pos numbers[9];
@@ -908,9 +906,9 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
  */
 
 /*
- * The format of the base64 blob is largely ssh2-packet-formatted,
+ * The format of the base64 blob is largely SSH-2-packet-formatted,
  * except that mpints are a bit different: they're more like the
- * old ssh1 mpint. You have a 32-bit bit count N, followed by
+ * old SSH-1 mpint. You have a 32-bit bit count N, followed by
  * (N+7)/8 bytes of data.
  * 
  * So. The blob contains:
@@ -922,7 +920,7 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
  *  - string encrypted-blob
  * 
  * (The first size field includes the size field itself and the
- * magic number before it. All other size fields are ordinary ssh2
+ * magic number before it. All other size fields are ordinary SSH-2
  * strings, so the size field indicates how much data is to
  * _follow_.)
  * 
@@ -967,7 +965,7 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
  * `dl-modp{sign{dsa' prefixes.
  * 
  * Finally, the encryption. The cipher-type string appears to be
- * either `none' or `3des-cbc'. Looks as if this is SSH2-style
+ * either `none' or `3des-cbc'. Looks as if this is SSH-2-style
  * 3des-cbc (i.e. outer cbc rather than inner). The key is created
  * from the passphrase by means of yet another hashing faff:
  * 
@@ -985,7 +983,8 @@ struct sshcom_key {
     int keyblob_len, keyblob_size;
 };
 
-static struct sshcom_key *load_sshcom_key(const Filename *filename)
+static struct sshcom_key *load_sshcom_key(const Filename *filename,
+                                         const char **errmsg_p)
 {
     struct sshcom_key *ret;
     FILE *fp;
@@ -1003,26 +1002,26 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename)
 
     fp = f_open(*filename, "r");
     if (!fp) {
-       errmsg = "Unable to open key file";
+       errmsg = "unable to open key file";
        goto error;
     }
     if (!fgets(buffer, sizeof(buffer), fp) ||
        0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) {
-       errmsg = "File does not begin with ssh.com key header";
+       errmsg = "file does not begin with ssh.com key header";
        goto error;
     }
 
     headers_done = 0;
     while (1) {
        if (!fgets(buffer, sizeof(buffer), fp)) {
-           errmsg = "Unexpected end of file";
+           errmsg = "unexpected end of file";
            goto error;
        }
         if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n"))
             break;                     /* done */
        if ((p = strchr(buffer, ':')) != NULL) {
            if (headers_done) {
-               errmsg = "Header found in body of key data";
+               errmsg = "header found in body of key data";
                goto error;
            }
            *p++ = '\0';
@@ -1034,11 +1033,11 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename)
             while ((len = strlen(p)) > (int)(sizeof(buffer) - (p-buffer) -1) ||
                    p[len-1] != '\n' || p[len-2] == '\\') {
                 if (len > (int)((p-buffer) + sizeof(buffer)-2)) {
-                    errmsg = "Header line too long to deal with";
+                    errmsg = "header line too long to deal with";
                     goto error;
                 }
                 if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) {
-                    errmsg = "Unexpected end of file";
+                    errmsg = "unexpected end of file";
                     goto error;
                 }
             }
@@ -1066,7 +1065,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename)
                     len = base64_decode_atom(base64_bit, out);
 
                     if (len <= 0) {
-                        errmsg = "Invalid base64 encoding";
+                        errmsg = "invalid base64 encoding";
                         goto error;
                     }
 
@@ -1086,10 +1085,11 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename)
     }
 
     if (ret->keyblob_len == 0 || !ret->keyblob) {
-       errmsg = "Key body not present";
+       errmsg = "key body not present";
        goto error;
     }
 
+    if (errmsg_p) *errmsg_p = NULL;
     return ret;
 
     error:
@@ -1101,12 +1101,13 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename)
         memset(&ret, 0, sizeof(ret));
        sfree(ret);
     }
+    if (errmsg_p) *errmsg_p = errmsg;
     return NULL;
 }
 
 int sshcom_encrypted(const Filename *filename, char **comment)
 {
-    struct sshcom_key *key = load_sshcom_key(filename);
+    struct sshcom_key *key = load_sshcom_key(filename, NULL);
     int pos, len, answer;
 
     *comment = NULL;
@@ -1186,9 +1187,10 @@ static int sshcom_put_mpint(void *target, void *data, int len)
     return len+4;
 }
 
-struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
+struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
+                                const char **errmsg_p)
 {
-    struct sshcom_key *key = load_sshcom_key(filename);
+    struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);
     char *errmsg;
     int pos, len;
     const char prefix_rsa[] = "if-modn{sign{rsa";
@@ -1200,7 +1202,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
     struct ssh2_userkey *ret = NULL, *retkey;
     const struct ssh_signkey *alg;
     unsigned char *blob = NULL;
-    int blobsize, publen, privlen;
+    int blobsize = 0, publen, privlen;
 
     if (!key)
         return NULL;
@@ -1209,7 +1211,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
      * Check magic number.
      */
     if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) {
-        errmsg = "Key does not begin with magic number";
+        errmsg = "key does not begin with magic number";
         goto error;
     }
 
@@ -1219,7 +1221,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
     pos = 8;
     if (key->keyblob_len < pos+4 ||
         (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
-        errmsg = "Key blob does not contain a key type string";
+        errmsg = "key blob does not contain a key type string";
         goto error;
     }
     if (len > sizeof(prefix_rsa) - 1 &&
@@ -1229,7 +1231,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
         !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) {
         type = DSA;
     } else {
-        errmsg = "Key is of unknown type";
+        errmsg = "key is of unknown type";
         goto error;
     }
     pos += 4+len;
@@ -1239,7 +1241,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
      */
     if (key->keyblob_len < pos+4 ||
         (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
-        errmsg = "Key blob does not contain a cipher type string";
+        errmsg = "key blob does not contain a cipher type string";
         goto error;
     }
     if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4))
@@ -1247,7 +1249,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
     else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8))
         encrypted = 1;
     else {
-        errmsg = "Key encryption is of unknown type";
+        errmsg = "key encryption is of unknown type";
         goto error;
     }
     pos += 4+len;
@@ -1257,13 +1259,13 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
      */
     if (key->keyblob_len < pos+4 ||
         (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
-        errmsg = "Key blob does not contain actual key data";
+        errmsg = "key blob does not contain actual key data";
         goto error;
     }
     ciphertext = (char *)key->keyblob + pos + 4;
     cipherlen = len;
     if (cipherlen == 0) {
-        errmsg = "Length of key data is zero";
+        errmsg = "length of key data is zero";
         goto error;
     }
 
@@ -1283,7 +1285,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
        unsigned char keybuf[32], iv[8];
 
         if (cipherlen % 8 != 0) {
-            errmsg = "Encrypted part of key is not a multiple of cipher block"
+            errmsg = "encrypted part of key is not a multiple of cipher block"
                 " size";
             goto error;
         }
@@ -1321,7 +1323,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
      * Strip away the containing string to get to the real meat.
      */
     len = GET_32BIT(ciphertext);
-    if (len > cipherlen-4) {
+    if (len < 0 || len > cipherlen-4) {
         errmsg = "containing string was ill-formed";
         goto error;
     }
@@ -1388,7 +1390,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
         publen = pos;
         pos += put_mp(blob+pos, x.start, x.bytes);
         privlen = pos - publen;
-    }
+    } else
+       return NULL;
 
     assert(privlen > 0);              /* should have bombed by now if not */
 
@@ -1414,6 +1417,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase)
     sfree(key->keyblob);
     memset(&key, 0, sizeof(key));
     sfree(key);
+    if (errmsg_p) *errmsg_p = errmsg;
     return ret;
 }