]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - import.c
first pass
[PuTTY.git] / import.c
index 5481ce8f141a80c5d12248832381261579f745eb..adf68777dcd96de71faef9e3de49f5a0aed0ddf1 100644 (file)
--- a/import.c
+++ b/import.c
@@ -20,6 +20,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
 struct ssh2_userkey *openssh_new_read(const Filename *filename,
                                       char *passphrase,
                                       const char **errmsg_p);
+int openssh_auto_write(const Filename *filename, struct ssh2_userkey *key,
+                       char *passphrase);
 int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
                       char *passphrase);
 int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
@@ -117,8 +119,8 @@ int export_ssh1(const Filename *filename, int type, struct RSAKey *key,
 int export_ssh2(const Filename *filename, int type,
                 struct ssh2_userkey *key, char *passphrase)
 {
-    if (type == SSH_KEYTYPE_OPENSSH_PEM)
-       return openssh_pem_write(filename, key, passphrase);
+    if (type == SSH_KEYTYPE_OPENSSH_AUTO)
+       return openssh_auto_write(filename, key, passphrase);
     if (type == SSH_KEYTYPE_OPENSSH_NEW)
        return openssh_new_write(filename, key, passphrase);
     if (type == SSH_KEYTYPE_SSHCOM)
@@ -193,14 +195,16 @@ static int ber_read_id_len(void *source, int sourcelen,
        return -1;
 
     if (*p & 0x80) {
+        unsigned len;
        int n = *p & 0x7F;
        p++, sourcelen--;
        if (sourcelen < n)
            return -1;
-       *length = 0;
+       len = 0;
        while (n--)
-           *length = (*length << 8) | (*p++);
+           len = (len << 8) | (*p++);
        sourcelen -= n;
+        *length = toint(len);
     } else {
        *length = *p;
        p++, sourcelen--;
@@ -360,7 +364,8 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
     struct openssh_pem_key *ret;
     FILE *fp = NULL;
     char *line = NULL;
-    char *errmsg, *p;
+    const char *errmsg;
+    char *p;
     int headers_done;
     char base64_bit[4];
     int base64_chars = 0;
@@ -380,8 +385,8 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
        goto error;
     }
     strip_crlf(line);
-    if (0 != strncmp(line, "-----BEGIN ", 11) ||
-       0 != strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) {
+    if (!strstartswith(line, "-----BEGIN ") ||
+        !strendswith(line, "PRIVATE KEY-----")) {
        errmsg = "file does not begin with OpenSSH key header";
        goto error;
     }
@@ -418,8 +423,8 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename,
            goto error;
        }
        strip_crlf(line);
-       if (0 == strncmp(line, "-----END ", 9) &&
-           0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) {
+       if (strstartswith(line, "-----END ") &&
+           strendswith(line, "PRIVATE KEY-----")) {
             sfree(line);
             line = NULL;
            break;                     /* done */
@@ -564,11 +569,11 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
 {
     struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p);
     struct ssh2_userkey *retkey;
-    unsigned char *p;
+    unsigned char *p, *q;
     int ret, id, len, flags;
     int i, num_integers;
     struct ssh2_userkey *retval = NULL;
-    char *errmsg;
+    const char *errmsg;
     unsigned char *blob;
     int blobsize = 0, blobptr, privptr;
     char *modptr = NULL;
@@ -654,7 +659,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
      * decrypt, if the key was encrypted. */
     ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
     p += ret;
-    if (ret < 0 || id != 16) {
+    if (ret < 0 || id != 16 || len < 0 ||
+        key->keyblob+key->keyblob_len-p < len) {
         errmsg = "ASN.1 decoding failure";
         retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
         goto error;
@@ -673,13 +679,15 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         /* And now for something completely different */
         unsigned char *priv;
         int privlen;
-        struct ec_curve *curve;
+        const struct ssh_signkey *alg;
+        const struct ec_curve *curve;
+        int algnamelen, curvenamelen;
         /* Read INTEGER 1 */
         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                               &id, &len, &flags);
         p += ret;
-        if (ret < 0 || id != 2 || key->keyblob+key->keyblob_len-p < len ||
-            len != 1 || p[0] != 1) {
+        if (ret < 0 || id != 2 || len != 1 ||
+            key->keyblob+key->keyblob_len-p < len || p[0] != 1) {
             errmsg = "ASN.1 decoding failure";
             retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
             goto error;
@@ -689,7 +697,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                               &id, &len, &flags);
         p += ret;
-        if (ret < 0 || id != 4 || key->keyblob+key->keyblob_len-p < len) {
+        if (ret < 0 || id != 4 || len < 0 ||
+            key->keyblob+key->keyblob_len-p < len) {
             errmsg = "ASN.1 decoding failure";
             retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
             goto error;
@@ -701,7 +710,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                               &id, &len, &flags);
         p += ret;
-        if (ret < 0 || id != 0 || key->keyblob+key->keyblob_len-p < len) {
+        if (ret < 0 || id != 0 || len < 0 ||
+            key->keyblob+key->keyblob_len-p < len) {
             errmsg = "ASN.1 decoding failure";
             retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
             goto error;
@@ -709,20 +719,14 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                               &id, &len, &flags);
         p += ret;
-        if (ret < 0 || id != 6 || key->keyblob+key->keyblob_len-p < len) {
+        if (ret < 0 || id != 6 || len < 0 ||
+            key->keyblob+key->keyblob_len-p < len) {
             errmsg = "ASN.1 decoding failure";
             retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
             goto error;
         }
-        if (len == 8 && !memcmp(p, nistp256_oid, nistp256_oid_len)) {
-            curve = ec_p256();
-        } else if (len == 5 && !memcmp(p, nistp384_oid,
-                                       nistp384_oid_len)) {
-            curve = ec_p384();
-        } else if (len == 5 && !memcmp(p, nistp521_oid,
-                                       nistp521_oid_len)) {
-            curve = ec_p521();
-        } else {
+        alg = ec_alg_by_oid(len, p, &curve);
+        if (!alg) {
             errmsg = "Unsupported ECDSA curve.";
             retval = NULL;
             goto error;
@@ -732,7 +736,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                               &id, &len, &flags);
         p += ret;
-        if (ret < 0 || id != 1 || key->keyblob+key->keyblob_len-p < len) {
+        if (ret < 0 || id != 1 || len < 0 ||
+            key->keyblob+key->keyblob_len-p < len) {
             errmsg = "ASN.1 decoding failure";
             retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
             goto error;
@@ -740,7 +745,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                               &id, &len, &flags);
         p += ret;
-        if (ret < 0 || id != 3 || key->keyblob+key->keyblob_len-p < len ||
+        if (ret < 0 || id != 3 || len < 0 ||
+            key->keyblob+key->keyblob_len-p < len ||
             len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) {
             errmsg = "ASN.1 decoding failure";
             retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
@@ -754,30 +760,42 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
             errmsg = "out of memory";
             goto error;
         }
-        if (curve->fieldBits == 256) {
-            retkey->alg = &ssh_ecdsa_nistp256;
-        } else if (curve->fieldBits == 384) {
-            retkey->alg = &ssh_ecdsa_nistp384;
-        } else {
-            retkey->alg = &ssh_ecdsa_nistp521;
-        }
-        blob = snewn((4+19 + 4+8 + 4+len) + (4+privlen), unsigned char);
+        retkey->alg = alg;
+        blob = snewn((4+19 + 4+8 + 4+len) + (4+1+privlen), unsigned char);
         if (!blob) {
             sfree(retkey);
             errmsg = "out of memory";
             goto error;
         }
-        PUT_32BIT(blob, 19);
-        sprintf((char*)blob+4, "ecdsa-sha2-nistp%d", curve->fieldBits);
-        PUT_32BIT(blob+4+19, 8);
-        sprintf((char*)blob+4+19+4, "nistp%d", curve->fieldBits);
-        PUT_32BIT(blob+4+19+4+8, len);
-        memcpy(blob+4+19+4+8+4, p, len);
-        PUT_32BIT(blob+4+19+4+8+4+len, privlen);
-        memcpy(blob+4+19+4+8+4+len+4, priv, privlen);
-        retkey->data = retkey->alg->createkey(blob, 4+19+4+8+4+len,
-                                              blob+4+19+4+8+4+len,
-                                              4+privlen);
+
+        q = blob;
+
+        algnamelen = strlen(alg->name);
+        PUT_32BIT(q, algnamelen); q += 4;
+        memcpy(q, alg->name, algnamelen); q += algnamelen;
+
+        curvenamelen = strlen(curve->name);
+        PUT_32BIT(q, curvenamelen); q += 4;
+        memcpy(q, curve->name, curvenamelen); q += curvenamelen;
+
+        PUT_32BIT(q, len); q += 4;
+        memcpy(q, p, len); q += len;
+
+        /*
+         * To be acceptable to our createkey(), the private blob must
+         * contain a valid mpint, i.e. without the top bit set. But
+         * the input private string may have the top bit set, so we
+         * prefix a zero byte to ensure createkey() doesn't fail for
+         * that reason.
+         */
+        PUT_32BIT(q, privlen+1);
+        q[4] = 0;
+        memcpy(q+5, priv, privlen);
+
+        retkey->data = retkey->alg->createkey(retkey->alg,
+                                              blob, q-blob,
+                                              q, 5+privlen);
+
         if (!retkey->data) {
             sfree(retkey);
             errmsg = "unable to create key data structure";
@@ -803,7 +821,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
             ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
                                   &id, &len, &flags);
             p += ret;
-            if (ret < 0 || id != 2 ||
+            if (ret < 0 || id != 2 || len < 0 ||
                 key->keyblob+key->keyblob_len-p < len) {
                 errmsg = "ASN.1 decoding failure";
                 retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL;
@@ -865,7 +883,7 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
         assert(privptr > 0);          /* should have bombed by now if not */
         retkey = snew(struct ssh2_userkey);
         retkey->alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
-        retkey->data = retkey->alg->createkey(blob, privptr,
+        retkey->data = retkey->alg->createkey(retkey->alg, blob, privptr,
                                               blob+privptr,
                                               blobptr-privptr);
         if (!retkey->data) {
@@ -876,6 +894,8 @@ struct ssh2_userkey *openssh_pem_read(const Filename *filename,
 
     } else {
         assert(0 && "Bad key type from load_openssh_pem_key");
+       errmsg = "Bad key type from load_openssh_pem_key";
+       goto error;
     }
 
     /*
@@ -909,7 +929,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
     int outlen;
     struct mpint_pos numbers[9];
     int nnumbers, pos, len, seqlen, i;
-    char *header, *footer;
+    const char *header, *footer;
     char zero[1];
     unsigned char iv[8];
     int ret = 0;
@@ -1056,7 +1076,7 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
     } else if (key->alg == &ssh_ecdsa_nistp256 ||
                key->alg == &ssh_ecdsa_nistp384 ||
                key->alg == &ssh_ecdsa_nistp521) {
-        unsigned char *oid;
+        const unsigned char *oid;
         int oidlen;
         int pointlen;
 
@@ -1070,28 +1090,9 @@ int openssh_pem_write(const Filename *filename, struct ssh2_userkey *key,
          *   [1]
          *     BIT STRING (0x00 public key point)
          */
-        switch (((struct ec_key *)key->data)->publicKey.curve->fieldBits) {
-          case 256:
-            /* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */
-            oid = nistp256_oid;
-            oidlen = nistp256_oid_len;
-            pointlen = 32 * 2;
-            break;
-          case 384:
-            /* OID: 1.3.132.0.34 (secp384r1) */
-            oid = nistp384_oid;
-            oidlen = nistp384_oid_len;
-            pointlen = 48 * 2;
-            break;
-          case 521:
-            /* OID: 1.3.132.0.35 (secp521r1) */
-            oid = nistp521_oid;
-            oidlen = nistp521_oid_len;
-            pointlen = 66 * 2;
-            break;
-          default:
-            assert(0);
-        }
+        oid = ec_alg_oid(key->alg, &oidlen);
+        pointlen = (((struct ec_key *)key->data)->publicKey.curve->fieldBits
+                    + 7) / 8 * 2;
 
         len = ber_write_id_len(NULL, 2, 1, 0);
         len += 1;
@@ -1301,7 +1302,8 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
     struct openssh_new_key *ret;
     FILE *fp = NULL;
     char *line = NULL;
-    char *errmsg, *p;
+    const char *errmsg;
+    char *p;
     char base64_bit[4];
     int base64_chars = 0;
     const void *filedata;
@@ -1541,18 +1543,14 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
                                       const char **errmsg_p)
 {
     struct openssh_new_key *key = load_openssh_new_key(filename, errmsg_p);
-    struct ssh2_userkey *retkey;
+    struct ssh2_userkey *retkey = NULL;
     int i;
     struct ssh2_userkey *retval = NULL;
-    char *errmsg;
-    unsigned char *blob;
-    int blobsize = 0;
+    const char *errmsg;
     unsigned checkint0, checkint1;
     const void *priv, *string;
     int privlen, stringlen, key_index;
-    const struct ssh_signkey *alg;
-
-    blob = NULL;
+    const struct ssh_signkey *alg = NULL;
 
     if (!key)
        return NULL;
@@ -1676,10 +1674,10 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
                            (const unsigned char *)thiskey);
         if (key_index == key->key_wanted) {
             retkey = snew(struct ssh2_userkey);
+            retkey->comment = NULL;
             retkey->alg = alg;
-            retkey->data = alg->openssh_createkey(&thiskey, &thiskeylen);
+            retkey->data = alg->openssh_createkey(alg, &thiskey, &thiskeylen);
             if (!retkey->data) {
-                sfree(retkey);
                 errmsg = "unable to create key data structure";
                 goto error;
             }
@@ -1716,11 +1714,16 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename,
 
     errmsg = NULL;                     /* no error */
     retval = retkey;
+    retkey = NULL;                     /* prevent the free */
 
     error:
-    if (blob) {
-        smemclr(blob, blobsize);
-        sfree(blob);
+    if (retkey) {
+        sfree(retkey->comment);
+        if (retkey->data) {
+            assert(alg);
+            alg->freekey(retkey->data);
+        }
+        sfree(retkey);
     }
     smemclr(key->keyblob, key->keyblob_size);
     sfree(key->keyblob);
@@ -1888,6 +1891,28 @@ int openssh_new_write(const Filename *filename, struct ssh2_userkey *key,
     return ret;
 }
 
+/* ----------------------------------------------------------------------
+ * The switch function openssh_auto_write(), which chooses one of the
+ * concrete OpenSSH output formats based on the key type.
+ */
+int openssh_auto_write(const Filename *filename, struct ssh2_userkey *key,
+                       char *passphrase)
+{
+    /*
+     * The old OpenSSH format supports a fixed list of key types. We
+     * assume that anything not in that fixed list is newer, and hence
+     * will use the new format.
+     */
+    if (key->alg == &ssh_dss ||
+        key->alg == &ssh_rsa ||
+        key->alg == &ssh_ecdsa_nistp256 ||
+        key->alg == &ssh_ecdsa_nistp384 ||
+        key->alg == &ssh_ecdsa_nistp521)
+        return openssh_pem_write(filename, key, passphrase);
+    else
+        return openssh_new_write(filename, key, passphrase);
+}
+
 /* ----------------------------------------------------------------------
  * Code to read ssh.com private keys.
  */
@@ -1977,7 +2002,8 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename,
     FILE *fp;
     char *line = NULL;
     int hdrstart, len;
-    char *errmsg, *p;
+    const char *errmsg;
+    char *p;
     int headers_done;
     char base64_bit[4];
     int base64_chars = 0;
@@ -2222,7 +2248,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
                                 const char **errmsg_p)
 {
     struct sshcom_key *key = load_sshcom_key(filename, errmsg_p);
-    char *errmsg;
+    const char *errmsg;
     int pos, len;
     const char prefix_rsa[] = "if-modn{sign{rsa";
     const char prefix_dsa[] = "dl-modp{sign{dsa";
@@ -2433,7 +2459,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
 
     retkey = snew(struct ssh2_userkey);
     retkey->alg = alg;
-    retkey->data = alg->createkey(blob, publen, blob+publen, privlen);
+    retkey->data = alg->createkey(alg, blob, publen, blob+publen, privlen);
     if (!retkey->data) {
        sfree(retkey);
        errmsg = "unable to create key data structure";
@@ -2466,7 +2492,7 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
     int outlen;
     struct mpint_pos numbers[6];
     int nnumbers, initial_zero, pos, lenpos, i;
-    char *type;
+    const char *type;
     char *ciphertext;
     int cipherlen;
     int ret = 0;
@@ -2562,7 +2588,7 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
     pos += 4;                         /* length field, fill in later */
     pos += put_string(outblob+pos, type, strlen(type));
     {
-       char *ciphertype = passphrase ? "3des-cbc" : "none";
+       const char *ciphertype = passphrase ? "3des-cbc" : "none";
        pos += put_string(outblob+pos, ciphertype, strlen(ciphertype));
     }
     lenpos = pos;                     /* remember this position */