X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/fb8344c97d27d06ad634a5f551b55d3d40097086..208a0f09f8b18915b46d6ad39fc7c2c8f62fb930:/import.c diff --git a/import.c b/import.c index 6c936ffc..55e3be27 100644 --- a/import.c +++ b/import.c @@ -109,6 +109,17 @@ int export_ssh2(const Filename *filename, int type, return 0; } +/* + * Strip trailing CRs and LFs at the end of a line of text. + */ +void strip_crlf(char *str) +{ + char *p = str + strlen(str); + + while (p > str && (p[-1] == '\r' || p[-1] == '\n')) + *--p = '\0'; +} + /* ---------------------------------------------------------------------- * Helper routines. (The base64 ones are defined in sshpubk.c.) */ @@ -297,9 +308,10 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret) */ enum { OSSH_DSA, OSSH_RSA }; +enum { OSSH_ENC_3DES, OSSH_ENC_AES }; struct openssh_key { int type; - int encrypted; + int encrypted, encryption; char iv[32]; unsigned char *keyblob; int keyblob_len, keyblob_size; @@ -310,7 +322,7 @@ static struct openssh_key *load_openssh_key(const Filename *filename, { struct openssh_key *ret; FILE *fp; - char buffer[256]; + char *line = NULL; char *errmsg, *p; int headers_done; char base64_bit[4]; @@ -322,73 +334,90 @@ static struct openssh_key *load_openssh_key(const Filename *filename, ret->encrypted = 0; memset(ret->iv, 0, sizeof(ret->iv)); - fp = f_open(*filename, "r"); + fp = f_open(filename, "r", FALSE); if (!fp) { 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")) { + + if (!(line = fgetline(fp))) { + errmsg = "unexpected end of file"; + goto error; + } + strip_crlf(line); + if (0 != strncmp(line, "-----BEGIN ", 11) || + 0 != strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) { errmsg = "file does not begin with OpenSSH key header"; goto error; } - if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n")) + if (!strcmp(line, "-----BEGIN RSA PRIVATE KEY-----")) ret->type = OSSH_RSA; - else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) + else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) ret->type = OSSH_DSA; else { errmsg = "unrecognised key type"; goto error; } + memset(line, 0, strlen(line)); + sfree(line); + line = NULL; headers_done = 0; while (1) { - if (!fgets(buffer, sizeof(buffer), fp)) { + if (!(line = fgetline(fp))) { errmsg = "unexpected end of file"; goto error; } - if (0 == strncmp(buffer, "-----END ", 9) && - 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) + strip_crlf(line); + if (0 == strncmp(line, "-----END ", 9) && + 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) break; /* done */ - if ((p = strchr(buffer, ':')) != NULL) { + if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; goto error; } *p++ = '\0'; while (*p && isspace((unsigned char)*p)) p++; - if (!strcmp(buffer, "Proc-Type")) { + if (!strcmp(line, "Proc-Type")) { if (p[0] != '4' || p[1] != ',') { errmsg = "Proc-Type is not 4 (only 4 is supported)"; goto error; } p += 2; - if (!strcmp(p, "ENCRYPTED\n")) + if (!strcmp(p, "ENCRYPTED")) ret->encrypted = 1; - } else if (!strcmp(buffer, "DEK-Info")) { - int i, j; - - if (strncmp(p, "DES-EDE3-CBC,", 13)) { - errmsg = "ciphers other than DES-EDE3-CBC not supported"; + } else if (!strcmp(line, "DEK-Info")) { + int i, j, ivlen; + + if (!strncmp(p, "DES-EDE3-CBC,", 13)) { + ret->encryption = OSSH_ENC_3DES; + ivlen = 8; + } else if (!strncmp(p, "AES-128-CBC,", 12)) { + ret->encryption = OSSH_ENC_AES; + ivlen = 16; + } else { + errmsg = "unsupported cipher"; goto error; } - p += 13; - for (i = 0; i < 8; i++) { - if (1 != sscanf(p, "%2x", &j)) - break; + p = strchr(p, ',') + 1;/* always non-NULL, by above checks */ + for (i = 0; i < ivlen; i++) { + if (1 != sscanf(p, "%2x", &j)) { + errmsg = "expected more iv data in DEK-Info"; + goto error; + } ret->iv[i] = j; p += 2; } - if (i < 8) { - errmsg = "expected 16-digit iv in DEK-Info"; + if (*p) { + errmsg = "more iv data than expected in DEK-Info"; goto error; } } } else { headers_done = 1; - p = buffer; + p = line; while (isbase64(*p)) { base64_bit[base64_chars++] = *p; if (base64_chars == 4) { @@ -419,6 +448,9 @@ static struct openssh_key *load_openssh_key(const Filename *filename, p++; } } + memset(line, 0, strlen(line)); + sfree(line); + line = NULL; } if (ret->keyblob_len == 0 || !ret->keyblob) { @@ -431,13 +463,16 @@ static struct openssh_key *load_openssh_key(const Filename *filename, goto error; } - memset(buffer, 0, sizeof(buffer)); memset(base64_bit, 0, sizeof(base64_bit)); if (errmsg_p) *errmsg_p = NULL; return ret; error: - memset(buffer, 0, sizeof(buffer)); + if (line) { + memset(line, 0, strlen(line)); + sfree(line); + line = NULL; + } memset(base64_bit, 0, sizeof(base64_bit)); if (ret) { if (ret->keyblob) { @@ -494,6 +529,10 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, * - let block B equal MD5(A || passphrase || iv) * - block C would be MD5(B || passphrase || iv) and so on * - encryption key is the first N bytes of A || B + * + * (Note that only 8 bytes of the iv are used for key + * derivation, even when the key is encrypted with AES and + * hence there are 16 bytes available.) */ struct MD5Context md5c; unsigned char keybuf[32]; @@ -512,8 +551,18 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, /* * Now decrypt the key blob. */ - des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, - key->keyblob, key->keyblob_len); + if (key->encryption == OSSH_ENC_3DES) + des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, + key->keyblob, key->keyblob_len); + else { + void *ctx; + assert(key->encryption == OSSH_ENC_AES); + ctx = aes_make_context(); + aes128_key(ctx, keybuf); + aes_iv(ctx, (unsigned char *)key->iv); + aes_ssh2_decrypt_blk(ctx, key->keyblob, key->keyblob_len); + aes_free_context(ctx); + } memset(&md5c, 0, sizeof(md5c)); memset(keybuf, 0, sizeof(keybuf)); @@ -827,6 +876,9 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, /* * Encrypt the key. + * + * For the moment, we still encrypt our OpenSSH keys using + * old-style 3DES. */ if (passphrase) { /* @@ -867,7 +919,7 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key, * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ - fp = f_open(*filename, "wb"); /* ensure Unix line endings */ + fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs(header, fp); @@ -989,8 +1041,8 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, { struct sshcom_key *ret; FILE *fp; - char buffer[256]; - int len; + char *line = NULL; + int hdrstart, len; char *errmsg, *p; int headers_done; char base64_bit[4]; @@ -1001,49 +1053,72 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, ret->keyblob = NULL; ret->keyblob_len = ret->keyblob_size = 0; - fp = f_open(*filename, "r"); + fp = f_open(filename, "r", FALSE); if (!fp) { errmsg = "unable to open key file"; goto error; } - if (!fgets(buffer, sizeof(buffer), fp) || - 0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) { + if (!(line = fgetline(fp))) { + errmsg = "unexpected end of file"; + goto error; + } + strip_crlf(line); + if (0 != strcmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) { errmsg = "file does not begin with ssh.com key header"; goto error; } + memset(line, 0, strlen(line)); + sfree(line); + line = NULL; headers_done = 0; while (1) { - if (!fgets(buffer, sizeof(buffer), fp)) { + if (!(line = fgetline(fp))) { errmsg = "unexpected end of file"; goto error; } - if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n")) + strip_crlf(line); + if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) break; /* done */ - if ((p = strchr(buffer, ':')) != NULL) { + if ((p = strchr(line, ':')) != NULL) { if (headers_done) { errmsg = "header found in body of key data"; goto error; } *p++ = '\0'; while (*p && isspace((unsigned char)*p)) p++; + hdrstart = p - line; + /* * Header lines can end in a trailing backslash for * continuation. */ - 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"; - goto error; - } - if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) { + len = hdrstart + strlen(line+hdrstart); + assert(!line[len]); + while (line[len-1] == '\\') { + char *line2; + int line2len; + + line2 = fgetline(fp); + if (!line2) { errmsg = "unexpected end of file"; goto error; } + strip_crlf(line2); + + line2len = strlen(line2); + line = sresize(line, len + line2len + 1, char); + strcpy(line + len - 1, line2); + len += line2len - 1; + assert(!line[len]); + + memset(line2, 0, strlen(line2)); + sfree(line2); + line2 = NULL; } - p[strcspn(p, "\n")] = '\0'; - if (!strcmp(buffer, "Comment")) { + p = line + hdrstart; + strip_crlf(p); + if (!strcmp(line, "Comment")) { /* Strip quotes in comment if present. */ if (p[0] == '"' && p[strlen(p)-1] == '"') { p++; @@ -1055,7 +1130,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, } else { headers_done = 1; - p = buffer; + p = line; while (isbase64(*p)) { base64_bit[base64_chars++] = *p; if (base64_chars == 4) { @@ -1083,6 +1158,9 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, p++; } } + memset(line, 0, strlen(line)); + sfree(line); + line = NULL; } if (ret->keyblob_len == 0 || !ret->keyblob) { @@ -1094,6 +1172,11 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, return ret; error: + if (line) { + memset(line, 0, strlen(line)); + sfree(line); + line = NULL; + } if (ret) { if (ret->keyblob) { memset(ret->keyblob, 0, ret->keyblob_size); @@ -1589,7 +1672,7 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key, * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. */ - fp = f_open(*filename, "wb"); /* ensure Unix line endings */ + fp = f_open(filename, "wb", TRUE); /* ensure Unix line endings */ if (!fp) goto error; fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);