X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=import.c;h=adf68777dcd96de71faef9e3de49f5a0aed0ddf1;hb=aa68c2872c3a8a43751d7fa13926af959dfc7cfb;hp=6206fdb110ac38f32bb7929add785d70c5f071a8;hpb=7cfa9f46270c4fa4e2b05b7a90ebd6a89d0ba460;p=PuTTY.git diff --git a/import.c b/import.c index 6206fdb1..adf68777 100644 --- 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; @@ -1628,8 +1626,8 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename, retkey = NULL; for (key_index = 0; key_index < key->nkeys; key_index++) { - unsigned char *thiskey; - int thiskeylen, npieces; + const unsigned char *thiskey; + int thiskeylen; /* * Read the key type, which will tell us how to scan over @@ -1647,35 +1645,25 @@ struct ssh2_userkey *openssh_new_read(const Filename *filename, * of strings, so we just need to know how many of them to * skip over. (The numbers below exclude the key comment.) */ - if (match_ssh_id(stringlen, string, "ssh-rsa")) { - alg = &ssh_rsa; - npieces = 6; /* n,e,d,iqmp,q,p */ - } else if (match_ssh_id(stringlen, string, "ssh-dss")) { - alg = &ssh_dss; - npieces = 5; /* p,q,g,y,x */ - } else if (match_ssh_id(stringlen, string, - "ecdsa-sha2-nistp256")) { - alg = &ssh_ecdsa_nistp256; - npieces = 3; /* curve name, point, private exponent */ - } else if (match_ssh_id(stringlen, string, - "ecdsa-sha2-nistp384")) { - alg = &ssh_ecdsa_nistp384; - npieces = 3; /* curve name, point, private exponent */ - } else if (match_ssh_id(stringlen, string, - "ecdsa-sha2-nistp521")) { - alg = &ssh_ecdsa_nistp521; - npieces = 3; /* curve name, point, private exponent */ - } else { - errmsg = "private key did not start with type string\n"; + { + /* find_pubkey_alg needs a zero-terminated copy of the + * algorithm name */ + char *name_zt = dupprintf("%.*s", stringlen, (char *)string); + alg = find_pubkey_alg(name_zt); + sfree(name_zt); + } + + if (!alg) { + errmsg = "private key type not recognised\n"; goto error; } - thiskey = (unsigned char *)priv; + thiskey = priv; /* * Skip over the pieces of key. */ - for (i = 0; i < npieces; i++) { + for (i = 0; i < alg->openssh_private_npieces; i++) { if (!(string = get_ssh_string(&privlen, &priv, &stringlen))) { errmsg = "ran out of data in mid-private-key"; goto error; @@ -1686,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; } @@ -1726,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); @@ -1898,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. */ @@ -1987,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; @@ -2232,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"; @@ -2443,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"; @@ -2476,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; @@ -2572,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 */