From cca372506522c1d588f9ebc66c6051089743d2a9 Mon Sep 17 00:00:00 2001 From: Sona Sarmadi Date: Wed, 2 Nov 2016 10:52:11 +0100 Subject: dropbear: fix multiple CVEs CVE-2016-7406 CVE-2016-7407 CVE-2016-7408 CVE-2016-7409 References: https://matt.ucc.asn.au/dropbear/CHANGES http://seclists.org/oss-sec/2016/q3/504 [YOCTO #10443] Signed-off-by: Sona Sarmadi Signed-off-by: Armin Kuster --- meta/recipes-core/dropbear/dropbear.inc | 4 + .../dropbear/dropbear/CVE-2016-7406.patch | 102 + .../dropbear/dropbear/CVE-2016-7407.patch | 2486 ++++++++++++++++++++ .../dropbear/dropbear/CVE-2016-7408.patch | 101 + .../dropbear/dropbear/CVE-2016-7409.patch | 27 + 5 files changed, 2720 insertions(+) create mode 100644 meta/recipes-core/dropbear/dropbear/CVE-2016-7406.patch create mode 100644 meta/recipes-core/dropbear/dropbear/CVE-2016-7407.patch create mode 100644 meta/recipes-core/dropbear/dropbear/CVE-2016-7408.patch create mode 100644 meta/recipes-core/dropbear/dropbear/CVE-2016-7409.patch diff --git a/meta/recipes-core/dropbear/dropbear.inc b/meta/recipes-core/dropbear/dropbear.inc index 923d31c307..ee2cd98845 100644 --- a/meta/recipes-core/dropbear/dropbear.inc +++ b/meta/recipes-core/dropbear/dropbear.inc @@ -17,6 +17,10 @@ SRC_URI = "http://matt.ucc.asn.au/dropbear/releases/dropbear-${PV}.tar.bz2 \ file://0003-configure.patch \ file://0004-fix-2kb-keys.patch \ file://0007-dropbear-fix-for-x32-abi.patch \ + file://CVE-2016-7406.patch \ + file://CVE-2016-7407.patch \ + file://CVE-2016-7408.patch \ + file://CVE-2016-7409.patch \ file://init \ file://dropbearkey.service \ file://dropbear@.service \ diff --git a/meta/recipes-core/dropbear/dropbear/CVE-2016-7406.patch b/meta/recipes-core/dropbear/dropbear/CVE-2016-7406.patch new file mode 100644 index 0000000000..a582d0ff81 --- /dev/null +++ b/meta/recipes-core/dropbear/dropbear/CVE-2016-7406.patch @@ -0,0 +1,102 @@ +From 8fd720c3e319da773b48c0b191f049dbd1e3c7f0 Mon Sep 17 00:00:00 2001 +From: Matt Johnston +Date: Mon, 11 Jul 2016 23:09:33 +0800 +Subject: [PATCH] Improve exit message formatting + +CVE: CVE-2016-7406 +Upstream-Status: Backport [backported from: +https://secure.ucc.asn.au/hg/dropbear/rev/b66a483f3dcb] + +Signed-off-by: Sona Sarmadi + +diff -ruN a/cli-main.c b/cli-main.c +--- a/cli-main.c 2016-03-09 15:54:53.000000000 +0100 ++++ b/cli-main.c 2016-10-20 12:49:00.323501119 +0200 +@@ -85,29 +85,30 @@ + #endif /* DBMULTI stuff */ + + static void cli_dropbear_exit(int exitcode, const char* format, va_list param) { ++ char exitmsg[150]; ++ char fullmsg[300]; + +- char fmtbuf[300]; +- char exitmsg[500]; ++ /* Note that exit message must be rendered before session cleanup */ + ++ /* Render the formatted exit message */ ++ vsnprintf(exitmsg, sizeof(exitmsg), format, param); ++ ++ /* Add the prefix depending on session/auth state */ + if (!sessinitdone) { +- snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", +- format); ++ snprintf(fullmsg, sizeof(fullmsg), "Exited: %s", exitmsg); + } else { +- snprintf(fmtbuf, sizeof(fmtbuf), ++ snprintf(fullmsg, sizeof(fullmsg), + "Connection to %s@%s:%s exited: %s", + cli_opts.username, cli_opts.remotehost, +- cli_opts.remoteport, format); ++ cli_opts.remoteport, exitmsg); + } + +- /* Arguments to the exit printout may be unsafe to use after session_cleanup() */ +- vsnprintf(exitmsg, sizeof(exitmsg), fmtbuf, param); +- + /* Do the cleanup first, since then the terminal will be reset */ + session_cleanup(); + /* Avoid printing onwards from terminal cruft */ + fprintf(stderr, "\n"); + +- dropbear_log(LOG_INFO, "%s", exitmsg);; ++ dropbear_log(LOG_INFO, "%s", fullmsg); + exit(exitcode); + } + +diff -ruN a/svr-session.c b/svr-session.c +--- a/svr-session.c 2016-03-09 15:54:54.000000000 +0100 ++++ b/svr-session.c 2016-10-20 13:27:20.629628336 +0200 +@@ -145,30 +145,33 @@ + /* failure exit - format must be <= 100 chars */ + void svr_dropbear_exit(int exitcode, const char* format, va_list param) { + +- char fmtbuf[300]; ++ char exitmsg[150]; ++ char fullmsg[300]; + int i; + ++ /* Render the formatted exit message */ ++ vsnprintf(exitmsg, sizeof(exitmsg), format, param); ++ ++ /* Add the prefix depending on session/auth state */ + if (!sessinitdone) { + /* before session init */ +- snprintf(fmtbuf, sizeof(fmtbuf), +- "Early exit: %s", format); ++ snprintf(fullmsg, sizeof(fullmsg), "Early exit: %s", exitmsg); + } else if (ses.authstate.authdone) { + /* user has authenticated */ +- snprintf(fmtbuf, sizeof(fmtbuf), ++ snprintf(fullmsg, sizeof(fullmsg), + "Exit (%s): %s", +- ses.authstate.pw_name, format); ++ ses.authstate.pw_name, exitmsg); + } else if (ses.authstate.pw_name) { + /* we have a potential user */ +- snprintf(fmtbuf, sizeof(fmtbuf), ++ snprintf(fullmsg, sizeof(fullmsg), + "Exit before auth (user '%s', %d fails): %s", +- ses.authstate.pw_name, ses.authstate.failcount, format); ++ ses.authstate.pw_name, ses.authstate.failcount, exitmsg); + } else { + /* before userauth */ +- snprintf(fmtbuf, sizeof(fmtbuf), +- "Exit before auth: %s", format); ++ snprintf(fullmsg, sizeof(fullmsg), "Exit before auth: %s", exitmsg); + } + +- _dropbear_log(LOG_INFO, fmtbuf, param); ++ dropbear_log(LOG_INFO, "%s", fullmsg); + + #ifdef USE_VFORK + /* For uclinux only the main server process should cleanup - we don't want diff --git a/meta/recipes-core/dropbear/dropbear/CVE-2016-7407.patch b/meta/recipes-core/dropbear/dropbear/CVE-2016-7407.patch new file mode 100644 index 0000000000..64113c112d --- /dev/null +++ b/meta/recipes-core/dropbear/dropbear/CVE-2016-7407.patch @@ -0,0 +1,2486 @@ + +# HG changeset patch +# User Matt Johnston +# Date 1468335601 -28800 +# Node ID 34e6127ef02eb52d1f1f9494b9cbfe89bec0e925 +# Parent 6914eedb10721db4833c8f005b4acd37f71fb975 +merge fixes from PuTTY import.c + +toint() from misc.c + +(revids are from hggit conversion) + +changeset: 4620:60a336a6c85c +user: Simon Tatham +date: Thu Feb 25 20:26:33 2016 +0000 +files: import.c +description: +Fix potential segfaults in reading OpenSSH's ASN.1 key format. + +The length coming back from ber_read_id_len might have overflowed, so +treat it as potentially negative. Also, while I'm here, accumulate it +inside ber_read_id_len as an unsigned, so as to avoid undefined +behaviour on integer overflow, and toint() it before return. + +Thanks to Hanno Böck for spotting this, with the aid of AFL. + +(cherry picked from commit 5b7833cd474a24ec098654dcba8cb9509f3bf2c1) + +Conflicts: + import.c + +(cherry-picker's note: resolving the conflict involved removing an +entire section of the original commit which fixed ECDSA code not +present on this branch) + + +changeset: 4619:9c6c638d98d8 +user: Simon Tatham +date: Sun Jul 14 10:45:54 2013 +0000 +files: import.c ssh.c sshdss.c sshpubk.c sshrsa.c +description: +Tighten up a lot of casts from unsigned to int which are read by one +of the GET_32BIT macros and then used as length fields. Missing bounds +checks against zero have been added, and also I've introduced a helper +function toint() which casts from unsigned to int in such a way as to +avoid C undefined behaviour, since I'm not sure I trust compilers any +more to do the obviously sensible thing. + +[originally from svn r9918] + + +changeset: 4618:3957829f24d3 +user: Simon Tatham +date: Mon Jul 08 22:36:04 2013 +0000 +files: import.c sshdss.c sshrsa.c +description: +Add an assortment of extra safety checks. + +[originally from svn r9896] + + +changeset: 4617:2cddee0bce12 +user: Jacob Nevins +date: Wed Dec 07 00:24:45 2005 +0000 +files: import.c +description: +Institutional failure to memset() things pointed at rather than pointers. +Things should now be zeroed and memory not leaked. Spotted by Brant Thomsen. + +[originally from svn r6476] + + +changeset: 4616:24ac78a9c71d +user: Simon Tatham +date: Wed Feb 11 13:58:27 2004 +0000 +files: import.c +description: +Jacob's last-minute testing found a couple of trivial bugs in +import.c, and my attempts to reproduce them in cmdgen found another +one there :-) + +[originally from svn r3847] + + +changeset: 4615:088d39a73db0 +user: Simon Tatham +date: Thu Jan 22 18:52:49 2004 +0000 +files: import.c +description: +Placate some gcc warnings. + +[originally from svn r3761] + + +changeset: 4614:e4288bad4d93 +parent: 1758:108b8924593d +user: Simon Tatham +date: Fri Oct 03 21:21:23 2003 +0000 +files: import.c +description: +My ASN.1 decoder returned wrong IDs for anything above 0x1E! Good +job it's never had to yet. Ahem. + +[originally from svn r3479] + + +CVE: CVE-2016-7407 +Upstream-Status: Backport [backported from: +https://secure.ucc.asn.au/hg/dropbear/rev/34e6127ef02e] + +Signed-off-by: Sona Sarmadi + +diff -r 6914eedb1072 -r 34e6127ef02e keyimport.c +--- a/keyimport.c Mon Jul 11 23:34:18 2016 +0800 ++++ b/keyimport.c Tue Jul 12 23:00:01 2016 +0800 +@@ -47,65 +47,67 @@ + (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])) ++ (((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])) + + static int openssh_encrypted(const char *filename); + static sign_key *openssh_read(const char *filename, char *passphrase); + static int openssh_write(const char *filename, sign_key *key, +- char *passphrase); ++ char *passphrase); + + static int dropbear_write(const char*filename, sign_key * key); + static sign_key *dropbear_read(const char* filename); + ++static int toint(unsigned u); ++ + #if 0 + static int sshcom_encrypted(const char *filename, char **comment); + static struct ssh2_userkey *sshcom_read(const char *filename, char *passphrase); + static int sshcom_write(const char *filename, struct ssh2_userkey *key, +- char *passphrase); ++ char *passphrase); + #endif + + int import_encrypted(const char* filename, int filetype) { + + if (filetype == KEYFILE_OPENSSH) { +- return openssh_encrypted(filename); ++ return openssh_encrypted(filename); + #if 0 + } else if (filetype == KEYFILE_SSHCOM) { + return sshcom_encrypted(filename, NULL); + #endif +- } +- return 0; ++ } ++ return 0; + } + + sign_key *import_read(const char *filename, char *passphrase, int filetype) { + + if (filetype == KEYFILE_OPENSSH) { +- return openssh_read(filename, passphrase); ++ return openssh_read(filename, passphrase); + } else if (filetype == KEYFILE_DROPBEAR) { + return dropbear_read(filename); + #if 0 + } else if (filetype == KEYFILE_SSHCOM) { +- return sshcom_read(filename, passphrase); ++ return sshcom_read(filename, passphrase); + #endif + } +- return NULL; ++ return NULL; + } + + int import_write(const char *filename, sign_key *key, char *passphrase, + int filetype) { + + if (filetype == KEYFILE_OPENSSH) { +- return openssh_write(filename, key, passphrase); ++ return openssh_write(filename, key, passphrase); + } else if (filetype == KEYFILE_DROPBEAR) { + return dropbear_write(filename, key); + #if 0 + } else if (filetype == KEYFILE_SSHCOM) { +- return sshcom_write(filename, key, passphrase); ++ return sshcom_write(filename, key, passphrase); + #endif + } +- return 0; ++ return 0; + } + + static sign_key *dropbear_read(const char* filename) { +@@ -183,11 +185,11 @@ + * Helper routines. (The base64 ones are defined in sshpubk.c.) + */ + +-#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \ +- ((c) >= 'a' && (c) <= 'z') || \ +- ((c) >= '0' && (c) <= '9') || \ +- (c) == '+' || (c) == '/' || (c) == '=' \ +- ) ++#define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \ ++ ((c) >= 'a' && (c) <= 'z') || \ ++ ((c) >= '0' && (c) <= '9') || \ ++ (c) == '+' || (c) == '/' || (c) == '=' \ ++ ) + + /* cpl has to be less than 100 */ + static void base64_encode_fp(FILE * fp, unsigned char *data, +@@ -220,57 +222,58 @@ + */ + + /* ASN.1 tag classes. */ +-#define ASN1_CLASS_UNIVERSAL (0 << 6) +-#define ASN1_CLASS_APPLICATION (1 << 6) ++#define ASN1_CLASS_UNIVERSAL (0 << 6) ++#define ASN1_CLASS_APPLICATION (1 << 6) + #define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6) +-#define ASN1_CLASS_PRIVATE (3 << 6) +-#define ASN1_CLASS_MASK (3 << 6) ++#define ASN1_CLASS_PRIVATE (3 << 6) ++#define ASN1_CLASS_MASK (3 << 6) + + /* Primitive versus constructed bit. */ +-#define ASN1_CONSTRUCTED (1 << 5) ++#define ASN1_CONSTRUCTED (1 << 5) + + static int ber_read_id_len(void *source, int sourcelen, +- int *id, int *length, int *flags) ++ int *id, int *length, int *flags) + { +- unsigned char *p = (unsigned char *) source; ++ unsigned char *p = (unsigned char *) source; + +- if (sourcelen == 0) ++ if (sourcelen == 0) ++ return -1; ++ ++ *flags = (*p & 0xE0); ++ if ((*p & 0x1F) == 0x1F) { ++ *id = 0; ++ while (*p & 0x80) { ++ p++, sourcelen--; ++ if (sourcelen == 0) + return -1; ++ *id = (*id << 7) | (*p & 0x7F); ++ } ++ p++, sourcelen--; ++ } else { ++ *id = *p & 0x1F; ++ p++, sourcelen--; ++ } + +- *flags = (*p & 0xE0); +- if ((*p & 0x1F) == 0x1F) { +- *id = 0; +- while (*p & 0x80) { +- *id = (*id << 7) | (*p & 0x7F); +- p++, sourcelen--; +- if (sourcelen == 0) +- return -1; +- } +- *id = (*id << 7) | (*p & 0x7F); +- p++, sourcelen--; +- } else { +- *id = *p & 0x1F; +- p++, sourcelen--; +- } ++ if (sourcelen == 0) ++ return -1; + +- if (sourcelen == 0) +- return -1; ++ if (*p & 0x80) { ++ unsigned len; ++ int n = *p & 0x7F; ++ p++, sourcelen--; ++ if (sourcelen < n) ++ return -1; ++ len = 0; ++ while (n--) ++ len = (len << 8) | (*p++); ++ sourcelen -= n; ++ *length = toint(len); ++ } else { ++ *length = *p; ++ p++, sourcelen--; ++ } + +- if (*p & 0x80) { +- int n = *p & 0x7F; +- p++, sourcelen--; +- if (sourcelen < n) +- return -1; +- *length = 0; +- while (n--) +- *length = (*length << 8) | (*p++); +- sourcelen -= n; +- } else { +- *length = *p; +- p++, sourcelen--; +- } +- +- return p - (unsigned char *) source; ++ return p - (unsigned char *) source; + } + + /* +@@ -281,57 +284,57 @@ + */ + static int ber_write_id_len(void *dest, int id, int length, int flags) + { +- unsigned char *d = (unsigned char *)dest; +- int len = 0; ++ unsigned char *d = (unsigned char *)dest; ++ int len = 0; + +- if (id <= 30) { +- /* +- * Identifier is one byte. +- */ +- len++; +- if (d) *d++ = id | flags; +- } else { +- int n; +- /* +- * Identifier is multiple bytes: the first byte is 11111 +- * plus the flags, and subsequent bytes encode the value of +- * the identifier, 7 bits at a time, with the top bit of +- * each byte 1 except the last one which is 0. +- */ +- len++; +- if (d) *d++ = 0x1F | flags; +- for (n = 1; (id >> (7*n)) > 0; n++) +- continue; /* count the bytes */ +- while (n--) { +- len++; +- if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F); +- } ++ if (id <= 30) { ++ /* ++ * Identifier is one byte. ++ */ ++ len++; ++ if (d) *d++ = id | flags; ++ } else { ++ int n; ++ /* ++ * Identifier is multiple bytes: the first byte is 11111 ++ * plus the flags, and subsequent bytes encode the value of ++ * the identifier, 7 bits at a time, with the top bit of ++ * each byte 1 except the last one which is 0. ++ */ ++ len++; ++ if (d) *d++ = 0x1F | flags; ++ for (n = 1; (id >> (7*n)) > 0; n++) ++ continue; /* count the bytes */ ++ while (n--) { ++ len++; ++ if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F); + } ++ } + +- if (length < 128) { +- /* +- * Length is one byte. +- */ +- len++; +- if (d) *d++ = length; +- } else { +- int n; +- /* +- * Length is multiple bytes. The first is 0x80 plus the +- * number of subsequent bytes, and the subsequent bytes +- * encode the actual length. +- */ +- for (n = 1; (length >> (8*n)) > 0; n++) +- continue; /* count the bytes */ +- len++; +- if (d) *d++ = 0x80 | n; +- while (n--) { +- len++; +- if (d) *d++ = (length >> (8*n)) & 0xFF; +- } ++ if (length < 128) { ++ /* ++ * Length is one byte. ++ */ ++ len++; ++ if (d) *d++ = length; ++ } else { ++ int n; ++ /* ++ * Length is multiple bytes. The first is 0x80 plus the ++ * number of subsequent bytes, and the subsequent bytes ++ * encode the actual length. ++ */ ++ for (n = 1; (length >> (8*n)) > 0; n++) ++ continue; /* count the bytes */ ++ len++; ++ if (d) *d++ = 0x80 | n; ++ while (n--) { ++ len++; ++ if (d) *d++ = (length >> (8*n)) & 0xFF; + } ++ } + +- return len; ++ return len; + } + + +@@ -344,99 +347,99 @@ + + enum { OSSH_DSA, OSSH_RSA, OSSH_EC }; + struct openssh_key { +- int type; +- int encrypted; +- char iv[32]; +- unsigned char *keyblob; ++ int type; ++ int encrypted; ++ char iv[32]; ++ unsigned char *keyblob; + unsigned int keyblob_len, keyblob_size; + }; + + static struct openssh_key *load_openssh_key(const char *filename) + { +- struct openssh_key *ret; ++ struct openssh_key *ret; + FILE *fp = NULL; +- char buffer[256]; ++ char buffer[256]; + char *errmsg = NULL, *p = NULL; +- int headers_done; ++ int headers_done; + unsigned long len, outlen; + + ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key)); +- ret->keyblob = NULL; +- ret->keyblob_len = ret->keyblob_size = 0; +- ret->encrypted = 0; +- memset(ret->iv, 0, sizeof(ret->iv)); ++ ret->keyblob = NULL; ++ ret->keyblob_len = ret->keyblob_size = 0; ++ ret->encrypted = 0; ++ memset(ret->iv, 0, sizeof(ret->iv)); + + if (strlen(filename) == 1 && filename[0] == '-') { + fp = stdin; + } else { + fp = fopen(filename, "r"); + } +- 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")) { +- errmsg = "File does not begin with OpenSSH key header"; +- goto error; +- } +- if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n")) +- ret->type = OSSH_RSA; +- else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) +- ret->type = OSSH_DSA; ++ 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")) { ++ errmsg = "File does not begin with OpenSSH key header"; ++ goto error; ++ } ++ if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n")) ++ ret->type = OSSH_RSA; ++ else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) ++ ret->type = OSSH_DSA; + else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n")) + ret->type = OSSH_EC; +- else { +- errmsg = "Unrecognised key type"; ++ else { ++ errmsg = "Unrecognised key type"; ++ goto error; ++ } ++ ++ headers_done = 0; ++ while (1) { ++ if (!fgets(buffer, sizeof(buffer), fp)) { ++ errmsg = "Unexpected end of file"; ++ goto error; ++ } ++ if (0 == strncmp(buffer, "-----END ", 9) && ++ 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) ++ break; /* done */ ++ if ((p = strchr(buffer, ':')) != 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 (p[0] != '4' || p[1] != ',') { ++ errmsg = "Proc-Type is not 4 (only 4 is supported)"; ++ goto error; ++ } ++ p += 2; ++ if (!strcmp(p, "ENCRYPTED\n")) ++ ret->encrypted = 1; ++ } else if (!strcmp(buffer, "DEK-Info")) { ++ int i, j; + +- headers_done = 0; +- while (1) { +- if (!fgets(buffer, sizeof(buffer), fp)) { +- errmsg = "Unexpected end of file"; +- goto error; ++ if (strncmp(p, "DES-EDE3-CBC,", 13)) { ++ errmsg = "Ciphers other than DES-EDE3-CBC not supported"; ++ goto error; + } +- if (0 == strncmp(buffer, "-----END ", 9) && +- 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) +- break; /* done */ +- if ((p = strchr(buffer, ':')) != 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 (p[0] != '4' || p[1] != ',') { +- errmsg = "Proc-Type is not 4 (only 4 is supported)"; +- goto error; +- } +- p += 2; +- if (!strcmp(p, "ENCRYPTED\n")) +- 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"; +- goto error; +- } +- p += 13; +- for (i = 0; i < 8; i++) { +- if (1 != sscanf(p, "%2x", &j)) +- break; +- ret->iv[i] = j; +- p += 2; +- } +- if (i < 8) { +- errmsg = "Expected 16-digit iv in DEK-Info"; +- goto error; +- } +- } +- } else { +- headers_done = 1; ++ p += 13; ++ for (i = 0; i < 8; i++) { ++ if (1 != sscanf(p, "%2x", &j)) ++ break; ++ ret->iv[i] = j; ++ p += 2; ++ } ++ if (i < 8) { ++ errmsg = "Expected 16-digit iv in DEK-Info"; ++ goto error; ++ } ++ } ++ } else { ++ headers_done = 1; + len = strlen(buffer); + outlen = len*4/3; + if (ret->keyblob_len + outlen > ret->keyblob_size) { +@@ -448,65 +451,65 @@ + if (base64_decode((const unsigned char *)buffer, len, + ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){ + errmsg = "Error decoding base64"; +- goto error; +- } ++ goto error; ++ } + ret->keyblob_len += outlen; +- } ++ } + } + +- if (ret->keyblob_len == 0 || !ret->keyblob) { +- errmsg = "Key body not present"; +- goto error; +- } ++ if (ret->keyblob_len == 0 || !ret->keyblob) { ++ 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"; +- goto error; +- } ++ if (ret->encrypted && ret->keyblob_len % 8 != 0) { ++ errmsg = "Encrypted key blob is not a multiple of cipher block size"; ++ goto error; ++ } + + m_burn(buffer, sizeof(buffer)); +- return ret; ++ return ret; + +- error: ++ error: + m_burn(buffer, sizeof(buffer)); +- if (ret) { +- if (ret->keyblob) { ++ if (ret) { ++ if (ret->keyblob) { + m_burn(ret->keyblob, ret->keyblob_size); + m_free(ret->keyblob); +- } ++ } + m_free(ret); + } + if (fp) { + fclose(fp); +- } ++ } + if (errmsg) { + fprintf(stderr, "Error: %s\n", errmsg); + } +- return NULL; ++ return NULL; + } + + static int openssh_encrypted(const char *filename) + { +- struct openssh_key *key = load_openssh_key(filename); +- int ret; ++ struct openssh_key *key = load_openssh_key(filename); ++ int ret; + +- if (!key) +- return 0; +- ret = key->encrypted; ++ if (!key) ++ return 0; ++ ret = key->encrypted; + m_burn(key->keyblob, key->keyblob_size); + m_free(key->keyblob); + m_free(key); +- return ret; ++ return ret; + } + + static sign_key *openssh_read(const char *filename, char * UNUSED(passphrase)) + { + struct openssh_key *key; +- unsigned char *p; +- int ret, id, len, flags; ++ unsigned char *p; ++ int ret, id, len, flags; + int i, num_integers = 0; + sign_key *retval = NULL; +- char *errmsg; ++ char *errmsg; + unsigned char *modptr = NULL; + int modlen = -9999; + enum signkey_type type; +@@ -518,86 +521,87 @@ + + key = load_openssh_key(filename); + +- if (!key) +- return NULL; ++ if (!key) ++ return NULL; + +- if (key->encrypted) { ++ if (key->encrypted) { + errmsg = "encrypted keys not supported currently"; + goto error; + #if 0 + /* matt TODO */ +- /* +- * Derive encryption key from passphrase and iv/salt: +- * +- * - let block A equal MD5(passphrase || iv) +- * - 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 +- */ +- struct MD5Context md5c; +- unsigned char keybuf[32]; ++ /* ++ * Derive encryption key from passphrase and iv/salt: ++ * ++ * - let block A equal MD5(passphrase || iv) ++ * - 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 ++ */ ++ struct MD5Context md5c; ++ unsigned char keybuf[32]; + +- MD5Init(&md5c); +- MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); +- MD5Update(&md5c, (unsigned char *)key->iv, 8); +- MD5Final(keybuf, &md5c); ++ MD5Init(&md5c); ++ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); ++ MD5Update(&md5c, (unsigned char *)key->iv, 8); ++ MD5Final(keybuf, &md5c); + +- MD5Init(&md5c); +- MD5Update(&md5c, keybuf, 16); +- MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); +- MD5Update(&md5c, (unsigned char *)key->iv, 8); +- MD5Final(keybuf+16, &md5c); +- +- /* +- * Now decrypt the key blob. +- */ +- des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, +- key->keyblob, key->keyblob_len); +- +- memset(&md5c, 0, sizeof(md5c)); +- memset(keybuf, 0, sizeof(keybuf)); +-#endif +- } ++ MD5Init(&md5c); ++ MD5Update(&md5c, keybuf, 16); ++ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); ++ MD5Update(&md5c, (unsigned char *)key->iv, 8); ++ MD5Final(keybuf+16, &md5c); + + /* +- * Now we have a decrypted key blob, which contains an ASN.1 +- * encoded private key. We must now untangle the ASN.1. +- * +- * We expect the whole key blob to be formatted as a SEQUENCE +- * (0x30 followed by a length code indicating that the rest of +- * the blob is part of the sequence). Within that SEQUENCE we +- * expect to see a bunch of INTEGERs. What those integers mean +- * depends on the key type: +- * +- * - For RSA, we expect the integers to be 0, n, e, d, p, q, +- * dmp1, dmq1, iqmp in that order. (The last three are d mod +- * (p-1), d mod (q-1), inverse of q mod p respectively.) +- * +- * - For DSA, we expect them to be 0, p, q, g, y, x in that +- * order. ++ * Now decrypt the key blob. + */ +- +- p = key->keyblob; ++ des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv, ++ key->keyblob, key->keyblob_len); + +- /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ +- ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); +- p += ret; +- if (ret < 0 || id != 16) { ++ memset(&md5c, 0, sizeof(md5c)); ++ memset(keybuf, 0, sizeof(keybuf)); ++#endif ++ } ++ ++ /* ++ * Now we have a decrypted key blob, which contains an ASN.1 ++ * encoded private key. We must now untangle the ASN.1. ++ * ++ * We expect the whole key blob to be formatted as a SEQUENCE ++ * (0x30 followed by a length code indicating that the rest of ++ * the blob is part of the sequence). Within that SEQUENCE we ++ * expect to see a bunch of INTEGERs. What those integers mean ++ * depends on the key type: ++ * ++ * - For RSA, we expect the integers to be 0, n, e, d, p, q, ++ * dmp1, dmq1, iqmp in that order. (The last three are d mod ++ * (p-1), d mod (q-1), inverse of q mod p respectively.) ++ * ++ * - For DSA, we expect them to be 0, p, q, g, y, x in that ++ * order. ++ */ ++ ++ p = key->keyblob; ++ ++ /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ ++ ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); ++ p += ret; ++ if (ret < 0 || id != 16 || len < 0 || ++ key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure - wrong password?"; +- goto error; +- } ++ goto error; ++ } + +- /* Expect a load of INTEGERs. */ +- if (key->type == OSSH_RSA) +- num_integers = 9; +- else if (key->type == OSSH_DSA) +- num_integers = 6; ++ /* Expect a load of INTEGERs. */ ++ if (key->type == OSSH_RSA) ++ num_integers = 9; ++ else if (key->type == OSSH_DSA) ++ num_integers = 6; + else if (key->type == OSSH_EC) + num_integers = 1; + +- /* +- * Space to create key blob in. +- */ ++ /* ++ * Space to create key blob in. ++ */ + blobbuf = buf_new(3000); + + #ifdef DROPBEAR_DSS +@@ -613,17 +617,17 @@ + } + #endif + +- for (i = 0; i < num_integers; i++) { +- 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) { +- errmsg = "ASN.1 decoding failure"; +- goto error; +- } ++ for (i = 0; i < num_integers; i++) { ++ ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, ++ &id, &len, &flags); ++ p += ret; ++ if (ret < 0 || id != 2 || len < 0 || ++ key->keyblob+key->keyblob_len-p < len) { ++ errmsg = "ASN.1 decoding failure"; ++ goto error; ++ } + +- if (i == 0) { ++ if (i == 0) { + /* First integer is a version indicator */ + int expected = -1; + switch (key->type) { +@@ -636,35 +640,35 @@ + break; + } + if (len != 1 || p[0] != expected) { +- errmsg = "Version number mismatch"; +- goto error; +- } +- } else if (key->type == OSSH_RSA) { +- /* ++ errmsg = "Version number mismatch"; ++ goto error; ++ } ++ } else if (key->type == OSSH_RSA) { ++ /* + * OpenSSH key order is n, e, d, p, q, dmp1, dmq1, iqmp + * but we want e, n, d, p, q +- */ +- if (i == 1) { +- /* Save the details for after we deal with number 2. */ ++ */ ++ if (i == 1) { ++ /* Save the details for after we deal with number 2. */ + modptr = p; +- modlen = len; ++ modlen = len; + } else if (i >= 2 && i <= 5) { + buf_putstring(blobbuf, (const char*)p, len); +- if (i == 2) { ++ if (i == 2) { + buf_putstring(blobbuf, (const char*)modptr, modlen); +- } +- } +- } else if (key->type == OSSH_DSA) { +- /* ++ } ++ } ++ } else if (key->type == OSSH_DSA) { ++ /* + * OpenSSH key order is p, q, g, y, x, + * we want the same. +- */ ++ */ + buf_putstring(blobbuf, (const char*)p, len); +- } ++ } + +- /* Skip past the number. */ +- p += len; +- } ++ /* Skip past the number. */ ++ p += len; ++ } + + #ifdef DROPBEAR_ECDSA + if (key->type == OSSH_EC) { +@@ -780,12 +784,12 @@ + } + #endif /* DROPBEAR_ECDSA */ + +- /* +- * Now put together the actual key. Simplest way to do this is +- * to assemble our own key blobs and feed them to the createkey +- * functions; this is a bit faffy but it does mean we get all +- * the sanity checks for free. +- */ ++ /* ++ * Now put together the actual key. Simplest way to do this is ++ * to assemble our own key blobs and feed them to the createkey ++ * functions; this is a bit faffy but it does mean we get all ++ * the sanity checks for free. ++ */ + if (key->type == OSSH_RSA || key->type == OSSH_DSA) { + buf_setpos(blobbuf, 0); + type = DROPBEAR_SIGNKEY_ANY; +@@ -794,18 +798,18 @@ + errmsg = "unable to create key structure"; + sign_key_free(retkey); + retkey = NULL; +- goto error; +- } ++ goto error; ++ } + } + +- errmsg = NULL; /* no error */ +- retval = retkey; ++ errmsg = NULL; /* no error */ ++ retval = retkey; + +- error: ++ error: + if (blobbuf) { + buf_burn(blobbuf); + buf_free(blobbuf); +- } ++ } + m_burn(key->keyblob, key->keyblob_size); + m_free(key->keyblob); + m_burn(key, sizeof(*key)); +@@ -813,22 +817,22 @@ + if (errmsg) { + fprintf(stderr, "Error: %s\n", errmsg); + } +- return retval; ++ return retval; + } + + static int openssh_write(const char *filename, sign_key *key, +- char *passphrase) ++ char *passphrase) + { + buffer * keyblob = NULL; + buffer * extrablob = NULL; /* used for calculated values to write */ + unsigned char *outblob = NULL; + int outlen = -9999; +- struct mpint_pos numbers[9]; ++ struct mpint_pos numbers[9]; + int nnumbers = -1, pos = 0, len = 0, seqlen, i; + char *header = NULL, *footer = NULL; +- char zero[1]; +- int ret = 0; +- FILE *fp; ++ char zero[1]; ++ int ret = 0; ++ FILE *fp; + + #ifdef DROPBEAR_RSA + mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ +@@ -843,9 +847,9 @@ + #endif + 0) + { +- /* +- * Fetch the key blobs. +- */ ++ /* ++ * Fetch the key blobs. ++ */ + keyblob = buf_new(3000); + buf_put_priv_key(keyblob, key, key->type); + +@@ -853,10 +857,10 @@ + /* skip the "ssh-rsa" or "ssh-dss" header */ + buf_incrpos(keyblob, buf_getint(keyblob)); + +- /* +- * Find the sequence of integers to be encoded into the OpenSSH +- * key blob, and also decide on the header line. +- */ ++ /* ++ * Find the sequence of integers to be encoded into the OpenSSH ++ * key blob, and also decide on the header line. ++ */ + numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; + + #ifdef DROPBEAR_RSA +@@ -871,17 +875,17 @@ + numbers[2].bytes = buf_getint(keyblob); + numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); + buf_incrpos(keyblob, numbers[2].bytes); +- ++ + /* n */ + numbers[1].bytes = buf_getint(keyblob); + numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); + buf_incrpos(keyblob, numbers[1].bytes); +- ++ + /* d */ + numbers[3].bytes = buf_getint(keyblob); + numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); + buf_incrpos(keyblob, numbers[3].bytes); +- ++ + /* p */ + numbers[4].bytes = buf_getint(keyblob); + numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); +@@ -949,9 +953,9 @@ + numbers[8].start = buf_getptr(extrablob, numbers[8].bytes); + buf_incrpos(extrablob, numbers[8].bytes); + +- nnumbers = 9; +- header = "-----BEGIN RSA PRIVATE KEY-----\n"; +- footer = "-----END RSA PRIVATE KEY-----\n"; ++ nnumbers = 9; ++ header = "-----BEGIN RSA PRIVATE KEY-----\n"; ++ footer = "-----END RSA PRIVATE KEY-----\n"; + } + #endif /* DROPBEAR_RSA */ + +@@ -983,45 +987,45 @@ + numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); + buf_incrpos(keyblob, numbers[5].bytes); + +- nnumbers = 6; +- header = "-----BEGIN DSA PRIVATE KEY-----\n"; +- footer = "-----END DSA PRIVATE KEY-----\n"; +- } ++ nnumbers = 6; ++ header = "-----BEGIN DSA PRIVATE KEY-----\n"; ++ footer = "-----END DSA PRIVATE KEY-----\n"; ++ } + #endif /* DROPBEAR_DSS */ + +- /* +- * Now count up the total size of the ASN.1 encoded integers, +- * so as to determine the length of the containing SEQUENCE. +- */ +- len = 0; +- for (i = 0; i < nnumbers; i++) { +- len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); +- len += numbers[i].bytes; +- } +- seqlen = len; +- /* Now add on the SEQUENCE header. */ +- len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); +- /* Round up to the cipher block size, ensuring we have at least one +- * byte of padding (see below). */ +- outlen = len; +- if (passphrase) +- outlen = (outlen+8) &~ 7; ++ /* ++ * Now count up the total size of the ASN.1 encoded integers, ++ * so as to determine the length of the containing SEQUENCE. ++ */ ++ len = 0; ++ for (i = 0; i < nnumbers; i++) { ++ len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); ++ len += numbers[i].bytes; ++ } ++ seqlen = len; ++ /* Now add on the SEQUENCE header. */ ++ len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); ++ /* Round up to the cipher block size, ensuring we have at least one ++ * byte of padding (see below). */ ++ outlen = len; ++ if (passphrase) ++ outlen = (outlen+8) &~ 7; + +- /* +- * Now we know how big outblob needs to be. Allocate it. +- */ ++ /* ++ * Now we know how big outblob needs to be. Allocate it. ++ */ + outblob = (unsigned char*)m_malloc(outlen); + +- /* +- * And write the data into it. +- */ +- pos = 0; +- pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); +- for (i = 0; i < nnumbers; i++) { +- pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); +- memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); +- pos += numbers[i].bytes; +- } ++ /* ++ * And write the data into it. ++ */ ++ pos = 0; ++ pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); ++ for (i = 0; i < nnumbers; i++) { ++ pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); ++ memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); ++ pos += numbers[i].bytes; ++ } + } /* end RSA and DSS handling */ + + #ifdef DROPBEAR_ECDSA +@@ -1116,40 +1120,40 @@ + } + #endif + +- /* +- * Padding on OpenSSH keys is deterministic. The number of +- * padding bytes is always more than zero, and always at most +- * the cipher block length. The value of each padding byte is +- * equal to the number of padding bytes. So a plaintext that's +- * an exact multiple of the block size will be padded with 08 +- * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a +- * plaintext one byte less than a multiple of the block size +- * will be padded with just 01. +- * +- * This enables the OpenSSL key decryption function to strip +- * off the padding algorithmically and return the unpadded +- * plaintext to the next layer: it looks at the final byte, and +- * then expects to find that many bytes at the end of the data +- * with the same value. Those are all removed and the rest is +- * returned. +- */ ++ /* ++ * Padding on OpenSSH keys is deterministic. The number of ++ * padding bytes is always more than zero, and always at most ++ * the cipher block length. The value of each padding byte is ++ * equal to the number of padding bytes. So a plaintext that's ++ * an exact multiple of the block size will be padded with 08 ++ * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a ++ * plaintext one byte less than a multiple of the block size ++ * will be padded with just 01. ++ * ++ * This enables the OpenSSL key decryption function to strip ++ * off the padding algorithmically and return the unpadded ++ * plaintext to the next layer: it looks at the final byte, and ++ * then expects to find that many bytes at the end of the data ++ * with the same value. Those are all removed and the rest is ++ * returned. ++ */ + dropbear_assert(pos == len); +- while (pos < outlen) { +- outblob[pos++] = outlen - len; +- } ++ while (pos < outlen) { ++ outblob[pos++] = outlen - len; ++ } + +- /* +- * Encrypt the key. +- */ +- if (passphrase) { ++ /* ++ * Encrypt the key. ++ */ ++ if (passphrase) { + fprintf(stderr, "Encrypted keys aren't supported currently\n"); + goto error; +- } ++ } + +- /* +- * And save it. We'll use Unix line endings just in case it's +- * subsequently transferred in binary mode. +- */ ++ /* ++ * And save it. We'll use Unix line endings just in case it's ++ * subsequently transferred in binary mode. ++ */ + if (strlen(filename) == 1 && filename[0] == '-') { + fp = stdout; + } else { +@@ -1157,28 +1161,28 @@ + } + if (!fp) { + fprintf(stderr, "Failed opening output file\n"); +- goto error; ++ goto error; + } +- fputs(header, fp); ++ fputs(header, fp); + base64_encode_fp(fp, outblob, outlen, 64); +- fputs(footer, fp); +- fclose(fp); +- ret = 1; ++ fputs(footer, fp); ++ fclose(fp); ++ ret = 1; + +- error: +- if (outblob) { +- memset(outblob, 0, outlen); ++ error: ++ if (outblob) { ++ memset(outblob, 0, outlen); + m_free(outblob); +- } ++ } + if (keyblob) { + buf_burn(keyblob); + buf_free(keyblob); +- } ++ } + if (extrablob) { + buf_burn(extrablob); + buf_free(extrablob); +- } +- return ret; ++ } ++ return ret; + } + + #if 0 +@@ -1196,10 +1200,10 @@ + * + * So. The blob contains: + * +- * - uint32 0x3f6ff9eb (magic number) +- * - uint32 size (total blob size) +- * - string key-type (see below) +- * - string cipher-type (tells you if key is encrypted) ++ * - uint32 0x3f6ff9eb (magic number) ++ * - uint32 size (total blob size) ++ * - string key-type (see below) ++ * - string cipher-type (tells you if key is encrypted) + * - string encrypted-blob + * + * (The first size field includes the size field itself and the +@@ -1255,654 +1259,679 @@ + * - first 16 bytes are MD5(passphrase) + * - next 16 bytes are MD5(passphrase || first 16 bytes) + * - if there were more, they'd be MD5(passphrase || first 32), +- * and so on. ++ * and so on. + */ + + #define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb + + struct sshcom_key { +- char comment[256]; /* allowing any length is overkill */ +- unsigned char *keyblob; +- int keyblob_len, keyblob_size; ++ char comment[256]; /* allowing any length is overkill */ ++ unsigned char *keyblob; ++ int keyblob_len, keyblob_size; + }; + + static struct sshcom_key *load_sshcom_key(const char *filename) + { +- struct sshcom_key *ret; +- FILE *fp; +- char buffer[256]; +- int len; +- char *errmsg, *p; +- int headers_done; +- char base64_bit[4]; +- int base64_chars = 0; ++ struct sshcom_key *ret; ++ FILE *fp; ++ char buffer[256]; ++ int len; ++ char *errmsg, *p; ++ int headers_done; ++ char base64_bit[4]; ++ int base64_chars = 0; + +- ret = snew(struct sshcom_key); +- ret->comment[0] = '\0'; +- ret->keyblob = NULL; +- ret->keyblob_len = ret->keyblob_size = 0; ++ ret = snew(struct sshcom_key); ++ ret->comment[0] = '\0'; ++ ret->keyblob = NULL; ++ ret->keyblob_len = ret->keyblob_size = 0; + + fp = fopen(filename, "r"); +- if (!fp) { +- errmsg = "Unable to open key file"; ++ 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")) { ++ 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"; ++ 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"; + goto error; ++ } ++ *p++ = '\0'; ++ while (*p && isspace((unsigned char)*p)) p++; ++ /* ++ * 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)) { ++ errmsg = "Unexpected end of file"; ++ goto error; ++ } ++ } ++ p[strcspn(p, "\n")] = '\0'; ++ if (!strcmp(buffer, "Comment")) { ++ /* Strip quotes in comment if present. */ ++ if (p[0] == '"' && p[strlen(p)-1] == '"') { ++ p++; ++ p[strlen(p)-1] = '\0'; ++ } ++ strncpy(ret->comment, p, sizeof(ret->comment)); ++ ret->comment[sizeof(ret->comment)-1] = '\0'; ++ } ++ } else { ++ headers_done = 1; ++ ++ p = buffer; ++ while (isbase64(*p)) { ++ base64_bit[base64_chars++] = *p; ++ if (base64_chars == 4) { ++ unsigned char out[3]; ++ ++ base64_chars = 0; ++ ++ len = base64_decode_atom(base64_bit, out); ++ ++ if (len <= 0) { ++ errmsg = "Invalid base64 encoding"; ++ goto error; ++ } ++ ++ if (ret->keyblob_len + len > ret->keyblob_size) { ++ ret->keyblob_size = ret->keyblob_len + len + 256; ++ ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, ++ unsigned char); ++ } ++ ++ memcpy(ret->keyblob + ret->keyblob_len, out, len); ++ ret->keyblob_len += len; ++ } ++ ++ p++; ++ } + } +- 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"; +- goto error; +- } ++ } + +- headers_done = 0; +- while (1) { +- if (!fgets(buffer, sizeof(buffer), fp)) { +- 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"; +- goto error; +- } +- *p++ = '\0'; +- while (*p && isspace((unsigned char)*p)) p++; +- /* +- * 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)) { +- errmsg = "Unexpected end of file"; +- goto error; +- } +- } +- p[strcspn(p, "\n")] = '\0'; +- if (!strcmp(buffer, "Comment")) { +- /* Strip quotes in comment if present. */ +- if (p[0] == '"' && p[strlen(p)-1] == '"') { +- p++; +- p[strlen(p)-1] = '\0'; +- } +- strncpy(ret->comment, p, sizeof(ret->comment)); +- ret->comment[sizeof(ret->comment)-1] = '\0'; +- } +- } else { +- headers_done = 1; ++ if (ret->keyblob_len == 0 || !ret->keyblob) { ++ errmsg = "Key body not present"; ++ goto error; ++ } + +- p = buffer; +- while (isbase64(*p)) { +- base64_bit[base64_chars++] = *p; +- if (base64_chars == 4) { +- unsigned char out[3]; ++ return ret; + +- base64_chars = 0; +- +- len = base64_decode_atom(base64_bit, out); +- +- if (len <= 0) { +- errmsg = "Invalid base64 encoding"; +- goto error; +- } +- +- if (ret->keyblob_len + len > ret->keyblob_size) { +- ret->keyblob_size = ret->keyblob_len + len + 256; +- ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, +- unsigned char); +- } +- +- memcpy(ret->keyblob + ret->keyblob_len, out, len); +- ret->keyblob_len += len; +- } +- +- p++; +- } +- } +- } +- +- if (ret->keyblob_len == 0 || !ret->keyblob) { +- errmsg = "Key body not present"; +- goto error; +- } +- +- return ret; +- +- error: +- if (ret) { +- if (ret->keyblob) { +- memset(ret->keyblob, 0, ret->keyblob_size); ++ error: ++ if (ret) { ++ if (ret->keyblob) { ++ memset(ret->keyblob, 0, ret->keyblob_size); + m_free(ret->keyblob); +- } +- memset(&ret, 0, sizeof(ret)); ++ } ++ memset(ret, 0, sizeof(*ret)); + m_free(ret); +- } +- return NULL; ++ } ++ return NULL; + } + + int sshcom_encrypted(const char *filename, char **comment) + { +- struct sshcom_key *key = load_sshcom_key(filename); +- int pos, len, answer; ++ struct sshcom_key *key = load_sshcom_key(filename); ++ int pos, len, answer; + +- *comment = NULL; +- if (!key) +- return 0; ++ *comment = NULL; ++ if (!key) ++ return 0; + +- /* +- * Check magic number. +- */ +- if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) +- return 0; /* key is invalid */ ++ /* ++ * Check magic number. ++ */ ++ if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) ++ return 0; /* key is invalid */ + +- /* +- * Find the cipher-type string. +- */ +- answer = 0; +- pos = 8; +- if (key->keyblob_len < pos+4) +- goto done; /* key is far too short */ +- pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */ +- if (key->keyblob_len < pos+4) +- goto done; /* key is far too short */ +- len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */ +- if (key->keyblob_len < pos+4+len) +- goto done; /* cipher type string is incomplete */ +- if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) +- answer = 1; ++ /* ++ * Find the cipher-type string. ++ */ ++ answer = 0; ++ pos = 8; ++ if (key->keyblob_len < pos+4) ++ goto done; /* key is far too short */ ++ len = toint(GET_32BIT(key->keyblob + pos)); ++ if (len < 0 || len > key->keyblob_len - pos - 4) ++ goto done; /* key is far too short */ ++ pos += 4 + len; /* skip key type */ ++ len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */ ++ if (len < 0 || len > key->keyblob_len - pos - 4) ++ goto done; /* cipher type string is incomplete */ ++ if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) ++ answer = 1; + +- done: +- *comment = dupstr(key->comment); +- memset(key->keyblob, 0, key->keyblob_size); ++ done: ++ *comment = dupstr(key->comment); ++ memset(key->keyblob, 0, key->keyblob_size); + m_free(key->keyblob); +- memset(&key, 0, sizeof(key)); ++ memset(key, 0, sizeof(*key)); + m_free(key); +- return answer; ++ return answer; + } + + static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) + { +- int bits; +- int bytes; +- unsigned char *d = (unsigned char *) data; ++ unsigned bits, bytes; ++ unsigned char *d = (unsigned char *) data; + +- if (len < 4) +- goto error; +- bits = GET_32BIT(d); ++ if (len < 4) ++ goto error; ++ bits = GET_32BIT(d); + +- bytes = (bits + 7) / 8; +- if (len < 4+bytes) +- goto error; ++ bytes = (bits + 7) / 8; ++ if (len < 4+bytes) ++ goto error; + +- ret->start = d + 4; +- ret->bytes = bytes; +- return bytes+4; ++ ret->start = d + 4; ++ ret->bytes = bytes; ++ return bytes+4; + +- error: +- ret->start = NULL; +- ret->bytes = -1; +- return len; /* ensure further calls fail as well */ ++ error: ++ ret->start = NULL; ++ ret->bytes = -1; ++ return len; /* ensure further calls fail as well */ + } + + static int sshcom_put_mpint(void *target, void *data, int len) + { +- unsigned char *d = (unsigned char *)target; +- unsigned char *i = (unsigned char *)data; +- int bits = len * 8 - 1; ++ unsigned char *d = (unsigned char *)target; ++ unsigned char *i = (unsigned char *)data; ++ int bits = len * 8 - 1; + +- while (bits > 0) { +- if (*i & (1 << (bits & 7))) +- break; +- if (!(bits-- & 7)) +- i++, len--; +- } ++ while (bits > 0) { ++ if (*i & (1 << (bits & 7))) ++ break; ++ if (!(bits-- & 7)) ++ i++, len--; ++ } + +- PUT_32BIT(d, bits+1); +- memcpy(d+4, i, len); +- return len+4; ++ PUT_32BIT(d, bits+1); ++ memcpy(d+4, i, len); ++ return len+4; + } + + sign_key *sshcom_read(const char *filename, char *passphrase) + { +- struct sshcom_key *key = load_sshcom_key(filename); +- char *errmsg; +- int pos, len; +- const char prefix_rsa[] = "if-modn{sign{rsa"; +- const char prefix_dsa[] = "dl-modp{sign{dsa"; +- enum { RSA, DSA } type; +- int encrypted; +- char *ciphertext; +- int cipherlen; +- struct ssh2_userkey *ret = NULL, *retkey; +- const struct ssh_signkey *alg; +- unsigned char *blob = NULL; +- int blobsize, publen, privlen; ++ struct sshcom_key *key = load_sshcom_key(filename); ++ char *errmsg; ++ int pos, len; ++ const char prefix_rsa[] = "if-modn{sign{rsa"; ++ const char prefix_dsa[] = "dl-modp{sign{dsa"; ++ enum { RSA, DSA } type; ++ int encrypted; ++ char *ciphertext; ++ int cipherlen; ++ struct ssh2_userkey *ret = NULL, *retkey; ++ const struct ssh_signkey *alg; ++ unsigned char *blob = NULL; ++ int blobsize = 0, publen, privlen; + +- if (!key) +- return NULL; ++ if (!key) ++ return NULL; ++ ++ /* ++ * Check magic number. ++ */ ++ if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) { ++ errmsg = "Key does not begin with magic number"; ++ goto error; ++ } ++ ++ /* ++ * Determine the key type. ++ */ ++ 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"; ++ goto error; ++ } ++ if (len > sizeof(prefix_rsa) - 1 && ++ !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) { ++ type = RSA; ++ } else if (len > sizeof(prefix_dsa) - 1 && ++ !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) { ++ type = DSA; ++ } else { ++ errmsg = "Key is of unknown type"; ++ goto error; ++ } ++ pos += 4+len; ++ ++ /* ++ * Determine the cipher type. ++ */ ++ 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"; ++ goto error; ++ } ++ if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4)) ++ encrypted = 0; ++ else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8)) ++ encrypted = 1; ++ else { ++ errmsg = "Key encryption is of unknown type"; ++ goto error; ++ } ++ pos += 4+len; ++ ++ /* ++ * Get hold of the encrypted part of the key. ++ */ ++ 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"; ++ goto error; ++ } ++ ciphertext = (char *)key->keyblob + pos + 4; ++ cipherlen = len; ++ if (cipherlen == 0) { ++ errmsg = "Length of key data is zero"; ++ goto error; ++ } ++ ++ /* ++ * Decrypt it if necessary. ++ */ ++ if (encrypted) { ++ /* ++ * Derive encryption key from passphrase and iv/salt: ++ * ++ * - let block A equal MD5(passphrase) ++ * - let block B equal MD5(passphrase || A) ++ * - block C would be MD5(passphrase || A || B) and so on ++ * - encryption key is the first N bytes of A || B ++ */ ++ struct MD5Context md5c; ++ unsigned char keybuf[32], iv[8]; ++ ++ if (cipherlen % 8 != 0) { ++ errmsg = "Encrypted part of key is not a multiple of cipher block" ++ " size"; ++ goto error; ++ } ++ ++ MD5Init(&md5c); ++ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); ++ MD5Final(keybuf, &md5c); ++ ++ MD5Init(&md5c); ++ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); ++ MD5Update(&md5c, keybuf, 16); ++ MD5Final(keybuf+16, &md5c); + + /* +- * Check magic number. ++ * Now decrypt the key blob. + */ +- if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) { +- errmsg = "Key does not begin with magic number"; +- goto error; +- } ++ memset(iv, 0, sizeof(iv)); ++ des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, ++ cipherlen); + +- /* +- * Determine the key type. +- */ +- 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"; +- goto error; +- } +- if (len > sizeof(prefix_rsa) - 1 && +- !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) { +- type = RSA; +- } else if (len > sizeof(prefix_dsa) - 1 && +- !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) { +- type = DSA; +- } else { +- errmsg = "Key is of unknown type"; +- goto error; +- } +- pos += 4+len; ++ memset(&md5c, 0, sizeof(md5c)); ++ memset(keybuf, 0, sizeof(keybuf)); + +- /* +- * Determine the cipher type. +- */ +- 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"; +- goto error; +- } +- if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4)) +- encrypted = 0; +- else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8)) +- encrypted = 1; +- else { +- errmsg = "Key encryption is of unknown type"; +- goto error; +- } +- pos += 4+len; ++ /* ++ * Hereafter we return WRONG_PASSPHRASE for any parsing ++ * error. (But only if we've just tried to decrypt it! ++ * Returning WRONG_PASSPHRASE for an unencrypted key is ++ * automatic doom.) ++ */ ++ if (encrypted) ++ ret = SSH2_WRONG_PASSPHRASE; ++ } + +- /* +- * Get hold of the encrypted part of the key. +- */ +- 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"; +- goto error; +- } +- ciphertext = (char *)key->keyblob + pos + 4; +- cipherlen = len; +- if (cipherlen == 0) { +- errmsg = "Length of key data is zero"; +- goto error; +- } ++ /* ++ * Strip away the containing string to get to the real meat. ++ */ ++ len = toint(GET_32BIT(ciphertext)); ++ if (len < 0 || len > cipherlen-4) { ++ errmsg = "containing string was ill-formed"; ++ goto error; ++ } ++ ciphertext += 4; ++ cipherlen = len; + +- /* +- * Decrypt it if necessary. +- */ +- if (encrypted) { +- /* +- * Derive encryption key from passphrase and iv/salt: +- * +- * - let block A equal MD5(passphrase) +- * - let block B equal MD5(passphrase || A) +- * - block C would be MD5(passphrase || A || B) and so on +- * - encryption key is the first N bytes of A || B +- */ +- struct MD5Context md5c; +- unsigned char keybuf[32], iv[8]; ++ /* ++ * Now we break down into RSA versus DSA. In either case we'll ++ * construct public and private blobs in our own format, and ++ * end up feeding them to alg->createkey(). ++ */ ++ blobsize = cipherlen + 256; ++ blob = snewn(blobsize, unsigned char); ++ privlen = 0; ++ if (type == RSA) { ++ struct mpint_pos n, e, d, u, p, q; ++ int pos = 0; ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); ++ if (!q.start) { ++ errmsg = "key data did not contain six integers"; ++ goto error; ++ } + +- if (cipherlen % 8 != 0) { +- errmsg = "Encrypted part of key is not a multiple of cipher block" +- " size"; +- goto error; +- } ++ alg = &ssh_rsa; ++ pos = 0; ++ pos += put_string(blob+pos, "ssh-rsa", 7); ++ pos += put_mp(blob+pos, e.start, e.bytes); ++ pos += put_mp(blob+pos, n.start, n.bytes); ++ publen = pos; ++ pos += put_string(blob+pos, d.start, d.bytes); ++ pos += put_mp(blob+pos, q.start, q.bytes); ++ pos += put_mp(blob+pos, p.start, p.bytes); ++ pos += put_mp(blob+pos, u.start, u.bytes); ++ privlen = pos - publen; ++ } else if (type == DSA) { ++ struct mpint_pos p, q, g, x, y; ++ int pos = 4; ++ if (GET_32BIT(ciphertext) != 0) { ++ errmsg = "predefined DSA parameters not supported"; ++ goto error; ++ } ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y); ++ pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x); ++ if (!x.start) { ++ errmsg = "key data did not contain five integers"; ++ goto error; ++ } + +- MD5Init(&md5c); +- MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); +- MD5Final(keybuf, &md5c); +- +- MD5Init(&md5c); +- MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); +- MD5Update(&md5c, keybuf, 16); +- MD5Final(keybuf+16, &md5c); +- +- /* +- * Now decrypt the key blob. +- */ +- memset(iv, 0, sizeof(iv)); +- des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, +- cipherlen); +- +- memset(&md5c, 0, sizeof(md5c)); +- memset(keybuf, 0, sizeof(keybuf)); +- +- /* +- * Hereafter we return WRONG_PASSPHRASE for any parsing +- * error. (But only if we've just tried to decrypt it! +- * Returning WRONG_PASSPHRASE for an unencrypted key is +- * automatic doom.) +- */ +- if (encrypted) +- ret = SSH2_WRONG_PASSPHRASE; +- } +- +- /* +- * Strip away the containing string to get to the real meat. +- */ +- len = GET_32BIT(ciphertext); +- if (len > cipherlen-4) { +- errmsg = "containing string was ill-formed"; +- goto error; +- } +- ciphertext += 4; +- cipherlen = len; +- +- /* +- * Now we break down into RSA versus DSA. In either case we'll +- * construct public and private blobs in our own format, and +- * end up feeding them to alg->createkey(). +- */ +- blobsize = cipherlen + 256; +- blob = snewn(blobsize, unsigned char); +- privlen = 0; +- if (type == RSA) { +- struct mpint_pos n, e, d, u, p, q; +- int pos = 0; +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); +- if (!q.start) { +- errmsg = "key data did not contain six integers"; +- goto error; +- } +- +- alg = &ssh_rsa; +- pos = 0; +- pos += put_string(blob+pos, "ssh-rsa", 7); +- pos += put_mp(blob+pos, e.start, e.bytes); +- pos += put_mp(blob+pos, n.start, n.bytes); +- publen = pos; +- pos += put_string(blob+pos, d.start, d.bytes); +- pos += put_mp(blob+pos, q.start, q.bytes); +- pos += put_mp(blob+pos, p.start, p.bytes); +- pos += put_mp(blob+pos, u.start, u.bytes); +- privlen = pos - publen; +- } else if (type == DSA) { +- struct mpint_pos p, q, g, x, y; +- int pos = 4; +- if (GET_32BIT(ciphertext) != 0) { +- errmsg = "predefined DSA parameters not supported"; +- goto error; +- } +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y); +- pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x); +- if (!x.start) { +- errmsg = "key data did not contain five integers"; +- goto error; +- } +- +- alg = &ssh_dss; +- pos = 0; +- pos += put_string(blob+pos, "ssh-dss", 7); +- pos += put_mp(blob+pos, p.start, p.bytes); +- pos += put_mp(blob+pos, q.start, q.bytes); +- pos += put_mp(blob+pos, g.start, g.bytes); +- pos += put_mp(blob+pos, y.start, y.bytes); +- publen = pos; +- pos += put_mp(blob+pos, x.start, x.bytes); +- privlen = pos - publen; +- } ++ alg = &ssh_dss; ++ pos = 0; ++ pos += put_string(blob+pos, "ssh-dss", 7); ++ pos += put_mp(blob+pos, p.start, p.bytes); ++ pos += put_mp(blob+pos, q.start, q.bytes); ++ pos += put_mp(blob+pos, g.start, g.bytes); ++ pos += put_mp(blob+pos, y.start, y.bytes); ++ publen = pos; ++ pos += put_mp(blob+pos, x.start, x.bytes); ++ privlen = pos - publen; ++ } else ++ return NULL; + + dropbear_assert(privlen > 0); /* should have bombed by now if not */ + +- retkey = snew(struct ssh2_userkey); +- retkey->alg = alg; +- retkey->data = alg->createkey(blob, publen, blob+publen, privlen); +- if (!retkey->data) { ++ retkey = snew(struct ssh2_userkey); ++ retkey->alg = alg; ++ retkey->data = alg->createkey(blob, publen, blob+publen, privlen); ++ if (!retkey->data) { + m_free(retkey); +- errmsg = "unable to create key data structure"; +- goto error; +- } +- retkey->comment = dupstr(key->comment); ++ errmsg = "unable to create key data structure"; ++ goto error; ++ } ++ retkey->comment = dupstr(key->comment); + +- errmsg = NULL; /* no error */ +- ret = retkey; ++ errmsg = NULL; /* no error */ ++ ret = retkey; + +- error: +- if (blob) { +- memset(blob, 0, blobsize); ++ error: ++ if (blob) { ++ memset(blob, 0, blobsize); + m_free(blob); +- } +- memset(key->keyblob, 0, key->keyblob_size); ++ } ++ memset(key->keyblob, 0, key->keyblob_size); + m_free(key->keyblob); +- memset(&key, 0, sizeof(key)); ++ memset(key, 0, sizeof(*key)); + m_free(key); +- return ret; ++ return ret; + } + + int sshcom_write(const char *filename, sign_key *key, +- char *passphrase) ++ char *passphrase) + { +- unsigned char *pubblob, *privblob; +- int publen, privlen; +- unsigned char *outblob; +- int outlen; +- struct mpint_pos numbers[6]; +- int nnumbers, initial_zero, pos, lenpos, i; +- char *type; +- char *ciphertext; +- int cipherlen; +- int ret = 0; +- FILE *fp; ++ unsigned char *pubblob, *privblob; ++ int publen, privlen; ++ unsigned char *outblob; ++ int outlen; ++ struct mpint_pos numbers[6]; ++ int nnumbers, initial_zero, pos, lenpos, i; ++ char *type; ++ char *ciphertext; ++ int cipherlen; ++ int ret = 0; ++ FILE *fp; + +- /* +- * Fetch the key blobs. +- */ +- pubblob = key->alg->public_blob(key->data, &publen); +- privblob = key->alg->private_blob(key->data, &privlen); +- outblob = NULL; ++ /* ++ * Fetch the key blobs. ++ */ ++ pubblob = key->alg->public_blob(key->data, &publen); ++ privblob = key->alg->private_blob(key->data, &privlen); ++ outblob = NULL; + +- /* +- * Find the sequence of integers to be encoded into the OpenSSH +- * key blob, and also decide on the header line. +- */ +- if (key->alg == &ssh_rsa) { +- int pos; +- struct mpint_pos n, e, d, p, q, iqmp; ++ /* ++ * Find the sequence of integers to be encoded into the OpenSSH ++ * key blob, and also decide on the header line. ++ */ ++ if (key->alg == &ssh_rsa) { ++ int pos; ++ struct mpint_pos n, e, d, p, q, iqmp; + +- pos = 4 + GET_32BIT(pubblob); +- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); +- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); +- pos = 0; +- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); +- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); +- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); +- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); ++ pos = 4 + GET_32BIT(pubblob); ++ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); ++ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); ++ pos = 0; ++ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); ++ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); ++ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); ++ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); + + dropbear_assert(e.start && iqmp.start); /* can't go wrong */ + +- numbers[0] = e; +- numbers[1] = d; +- numbers[2] = n; +- numbers[3] = iqmp; +- numbers[4] = q; +- numbers[5] = p; ++ numbers[0] = e; ++ numbers[1] = d; ++ numbers[2] = n; ++ numbers[3] = iqmp; ++ numbers[4] = q; ++ numbers[5] = p; + +- nnumbers = 6; +- initial_zero = 0; +- type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; +- } else if (key->alg == &ssh_dss) { +- int pos; +- struct mpint_pos p, q, g, y, x; ++ nnumbers = 6; ++ initial_zero = 0; ++ type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; ++ } else if (key->alg == &ssh_dss) { ++ int pos; ++ struct mpint_pos p, q, g, y, x; + +- pos = 4 + GET_32BIT(pubblob); +- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); +- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); +- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); +- pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); +- pos = 0; +- pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); ++ pos = 4 + GET_32BIT(pubblob); ++ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); ++ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); ++ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); ++ pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); ++ pos = 0; ++ pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); + + dropbear_assert(y.start && x.start); /* can't go wrong */ + +- numbers[0] = p; +- numbers[1] = g; +- numbers[2] = q; +- numbers[3] = y; +- numbers[4] = x; ++ numbers[0] = p; ++ numbers[1] = g; ++ numbers[2] = q; ++ numbers[3] = y; ++ numbers[4] = x; + +- nnumbers = 5; +- initial_zero = 1; +- type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}"; +- } else { ++ nnumbers = 5; ++ initial_zero = 1; ++ type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}"; ++ } else { + dropbear_assert(0); /* zoinks! */ +- } ++ } + +- /* +- * Total size of key blob will be somewhere under 512 plus +- * combined length of integers. We'll calculate the more +- * precise size as we construct the blob. +- */ +- outlen = 512; +- for (i = 0; i < nnumbers; i++) +- outlen += 4 + numbers[i].bytes; +- outblob = snewn(outlen, unsigned char); ++ /* ++ * Total size of key blob will be somewhere under 512 plus ++ * combined length of integers. We'll calculate the more ++ * precise size as we construct the blob. ++ */ ++ outlen = 512; ++ for (i = 0; i < nnumbers; i++) ++ outlen += 4 + numbers[i].bytes; ++ outblob = snewn(outlen, unsigned char); + +- /* +- * Create the unencrypted key blob. +- */ +- pos = 0; +- PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4; +- pos += 4; /* length field, fill in later */ +- pos += put_string(outblob+pos, type, strlen(type)); +- { +- char *ciphertype = passphrase ? "3des-cbc" : "none"; +- pos += put_string(outblob+pos, ciphertype, strlen(ciphertype)); +- } +- lenpos = pos; /* remember this position */ +- pos += 4; /* encrypted-blob size */ +- pos += 4; /* encrypted-payload size */ +- if (initial_zero) { +- PUT_32BIT(outblob+pos, 0); +- pos += 4; +- } +- for (i = 0; i < nnumbers; i++) +- pos += sshcom_put_mpint(outblob+pos, +- numbers[i].start, numbers[i].bytes); +- /* Now wrap up the encrypted payload. */ +- PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8)); +- /* Pad encrypted blob to a multiple of cipher block size. */ +- if (passphrase) { +- int padding = -(pos - (lenpos+4)) & 7; +- while (padding--) +- outblob[pos++] = random_byte(); +- } +- ciphertext = (char *)outblob+lenpos+4; +- cipherlen = pos - (lenpos+4); ++ /* ++ * Create the unencrypted key blob. ++ */ ++ pos = 0; ++ PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4; ++ pos += 4; /* length field, fill in later */ ++ pos += put_string(outblob+pos, type, strlen(type)); ++ { ++ char *ciphertype = passphrase ? "3des-cbc" : "none"; ++ pos += put_string(outblob+pos, ciphertype, strlen(ciphertype)); ++ } ++ lenpos = pos; /* remember this position */ ++ pos += 4; /* encrypted-blob size */ ++ pos += 4; /* encrypted-payload size */ ++ if (initial_zero) { ++ PUT_32BIT(outblob+pos, 0); ++ pos += 4; ++ } ++ for (i = 0; i < nnumbers; i++) ++ pos += sshcom_put_mpint(outblob+pos, ++ numbers[i].start, numbers[i].bytes); ++ /* Now wrap up the encrypted payload. */ ++ PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8)); ++ /* Pad encrypted blob to a multiple of cipher block size. */ ++ if (passphrase) { ++ int padding = -(pos - (lenpos+4)) & 7; ++ while (padding--) ++ outblob[pos++] = random_byte(); ++ } ++ ciphertext = (char *)outblob+lenpos+4; ++ cipherlen = pos - (lenpos+4); + dropbear_assert(!passphrase || cipherlen % 8 == 0); +- /* Wrap up the encrypted blob string. */ +- PUT_32BIT(outblob+lenpos, cipherlen); +- /* And finally fill in the total length field. */ +- PUT_32BIT(outblob+4, pos); ++ /* Wrap up the encrypted blob string. */ ++ PUT_32BIT(outblob+lenpos, cipherlen); ++ /* And finally fill in the total length field. */ ++ PUT_32BIT(outblob+4, pos); + + dropbear_assert(pos < outlen); + ++ /* ++ * Encrypt the key. ++ */ ++ if (passphrase) { + /* +- * Encrypt the key. ++ * Derive encryption key from passphrase and iv/salt: ++ * ++ * - let block A equal MD5(passphrase) ++ * - let block B equal MD5(passphrase || A) ++ * - block C would be MD5(passphrase || A || B) and so on ++ * - encryption key is the first N bytes of A || B + */ +- if (passphrase) { +- /* +- * Derive encryption key from passphrase and iv/salt: +- * +- * - let block A equal MD5(passphrase) +- * - let block B equal MD5(passphrase || A) +- * - block C would be MD5(passphrase || A || B) and so on +- * - encryption key is the first N bytes of A || B +- */ +- struct MD5Context md5c; +- unsigned char keybuf[32], iv[8]; ++ struct MD5Context md5c; ++ unsigned char keybuf[32], iv[8]; + +- MD5Init(&md5c); +- MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); +- MD5Final(keybuf, &md5c); ++ MD5Init(&md5c); ++ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); ++ MD5Final(keybuf, &md5c); + +- MD5Init(&md5c); +- MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); +- MD5Update(&md5c, keybuf, 16); +- MD5Final(keybuf+16, &md5c); +- +- /* +- * Now decrypt the key blob. +- */ +- memset(iv, 0, sizeof(iv)); +- des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, +- cipherlen); +- +- memset(&md5c, 0, sizeof(md5c)); +- memset(keybuf, 0, sizeof(keybuf)); +- } ++ MD5Init(&md5c); ++ MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); ++ MD5Update(&md5c, keybuf, 16); ++ MD5Final(keybuf+16, &md5c); + + /* +- * And save it. We'll use Unix line endings just in case it's +- * subsequently transferred in binary mode. ++ * Now decrypt the key blob. + */ ++ memset(iv, 0, sizeof(iv)); ++ des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, ++ cipherlen); ++ ++ memset(&md5c, 0, sizeof(md5c)); ++ memset(keybuf, 0, sizeof(keybuf)); ++ } ++ ++ /* ++ * And save it. We'll use Unix line endings just in case it's ++ * subsequently transferred in binary mode. ++ */ + fp = fopen(filename, "wb"); /* ensure Unix line endings */ +- if (!fp) +- goto error; +- fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); +- fprintf(fp, "Comment: \""); +- /* +- * Comment header is broken with backslash-newline if it goes +- * over 70 chars. Although it's surrounded by quotes, it +- * _doesn't_ escape backslashes or quotes within the string. +- * Don't ask me, I didn't design it. +- */ +- { +- int slen = 60; /* starts at 60 due to "Comment: " */ +- char *c = key->comment; +- while ((int)strlen(c) > slen) { +- fprintf(fp, "%.*s\\\n", slen, c); +- c += slen; +- slen = 70; /* allow 70 chars on subsequent lines */ +- } +- fprintf(fp, "%s\"\n", c); ++ if (!fp) ++ goto error; ++ fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); ++ fprintf(fp, "Comment: \""); ++ /* ++ * Comment header is broken with backslash-newline if it goes ++ * over 70 chars. Although it's surrounded by quotes, it ++ * _doesn't_ escape backslashes or quotes within the string. ++ * Don't ask me, I didn't design it. ++ */ ++ { ++ int slen = 60; /* starts at 60 due to "Comment: " */ ++ char *c = key->comment; ++ while ((int)strlen(c) > slen) { ++ fprintf(fp, "%.*s\\\n", slen, c); ++ c += slen; ++ slen = 70; /* allow 70 chars on subsequent lines */ + } ++ fprintf(fp, "%s\"\n", c); ++ } + base64_encode_fp(fp, outblob, pos, 70); +- fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); +- fclose(fp); +- ret = 1; ++ fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); ++ fclose(fp); ++ ret = 1; + +- error: +- if (outblob) { +- memset(outblob, 0, outlen); ++ error: ++ if (outblob) { ++ memset(outblob, 0, outlen); + m_free(outblob); +- } +- if (privblob) { +- memset(privblob, 0, privlen); ++ } ++ if (privblob) { ++ memset(privblob, 0, privlen); + m_free(privblob); +- } +- if (pubblob) { +- memset(pubblob, 0, publen); ++ } ++ if (pubblob) { ++ memset(pubblob, 0, publen); + m_free(pubblob); +- } +- return ret; ++ } ++ return ret; + } + #endif /* ssh.com stuff disabled */ ++ ++/* From PuTTY misc.c */ ++static int toint(unsigned u) ++{ ++ /* ++ * Convert an unsigned to an int, without running into the ++ * undefined behaviour which happens by the strict C standard if ++ * the value overflows. You'd hope that sensible compilers would ++ * do the sensible thing in response to a cast, but actually I ++ * don't trust modern compilers not to do silly things like ++ * assuming that _obviously_ you wouldn't have caused an overflow ++ * and so they can elide an 'if (i < 0)' test immediately after ++ * the cast. ++ * ++ * Sensible compilers ought of course to optimise this entire ++ * function into 'just return the input value'! ++ */ ++ if (u <= (unsigned)INT_MAX) ++ return (int)u; ++ else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */ ++ return INT_MIN + (int)(u - (unsigned)INT_MIN); ++ else ++ return INT_MIN; /* fallback; should never occur on binary machines */ ++} + diff --git a/meta/recipes-core/dropbear/dropbear/CVE-2016-7408.patch b/meta/recipes-core/dropbear/dropbear/CVE-2016-7408.patch new file mode 100644 index 0000000000..38ad8c3481 --- /dev/null +++ b/meta/recipes-core/dropbear/dropbear/CVE-2016-7408.patch @@ -0,0 +1,101 @@ + +# HG changeset patch +# User Matt Johnston +# Date 1468248038 -28800 +# Node ID eed9376a4ad68e3ae7f17d154dbf126ee66c54bc +# Parent 6a14b1f6dc04e70933c49ea335184e68c1deeb94 +improve algorithm list parsing + +CVE: CVE-2016-7408 +Upstream-Status: Backport [backported from: +https://secure.ucc.asn.au/hg/dropbear/rev/eed9376a4ad6] + +Signed-off-by: Sona Sarmadi + +diff -r 6a14b1f6dc04 -r eed9376a4ad6 common-algo.c +--- a/common-algo.c Mon Jul 11 21:51:25 2016 +0800 ++++ b/common-algo.c Mon Jul 11 22:40:38 2016 +0800 +@@ -531,21 +531,6 @@ + return NULL; + } + +-static void +-try_add_algo(const char *algo_name, algo_type *algos, +- const char *algo_desc, algo_type * new_algos, int *num_ret) +-{ +- algo_type *match_algo = check_algo(algo_name, algos); +- if (!match_algo) +- { +- dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", algo_name, algo_desc); +- return; +- } +- +- new_algos[*num_ret] = *match_algo; +- (*num_ret)++; +-} +- + /* Checks a user provided comma-separated algorithm list for available + * options. Any that are not acceptable are removed in-place. Returns the + * number of valid algorithms. */ +@@ -553,30 +538,43 @@ + check_user_algos(const char* user_algo_list, algo_type * algos, + const char *algo_desc) + { +- algo_type new_algos[MAX_PROPOSED_ALGO]; +- /* this has two passes. first we sweep through the given list of +- * algorithms and mark them as usable=2 in the algo_type[] array... */ +- int num_ret = 0; ++ algo_type new_algos[MAX_PROPOSED_ALGO+1]; + char *work_list = m_strdup(user_algo_list); +- char *last_name = work_list; ++ char *start = work_list; + char *c; +- for (c = work_list; *c; c++) ++ int n; ++ /* So we can iterate and look for null terminator */ ++ memset(new_algos, 0x0, sizeof(new_algos)); ++ for (c = work_list, n = 0; ; c++) + { +- if (*c == ',') +- { ++ char oc = *c; ++ if (n >= MAX_PROPOSED_ALGO) { ++ dropbear_exit("Too many algorithms '%s'", user_algo_list); ++ } ++ if (*c == ',' || *c == '\0') { ++ algo_type *match_algo = NULL; + *c = '\0'; +- try_add_algo(last_name, algos, algo_desc, new_algos, &num_ret); ++ match_algo = check_algo(start, algos); ++ if (match_algo) { ++ if (check_algo(start, new_algos)) { ++ TRACE(("Skip repeated algorithm '%s'", start)) ++ } else { ++ new_algos[n] = *match_algo; ++ n++; ++ } ++ } else { ++ dropbear_log(LOG_WARNING, "This Dropbear program does not support '%s' %s algorithm", start, algo_desc); ++ } + c++; +- last_name = c; ++ start = c; ++ } ++ if (oc == '\0') { ++ break; + } + } +- try_add_algo(last_name, algos, algo_desc, new_algos, &num_ret); + m_free(work_list); +- +- new_algos[num_ret].name = NULL; +- +- /* Copy one more as a blank delimiter */ +- memcpy(algos, new_algos, sizeof(*new_algos) * (num_ret+1)); +- return num_ret; ++ /* n+1 to include a null terminator */ ++ memcpy(algos, new_algos, sizeof(*new_algos) * (n+1)); ++ return n; + } + #endif /* ENABLE_USER_ALGO_LIST */ + diff --git a/meta/recipes-core/dropbear/dropbear/CVE-2016-7409.patch b/meta/recipes-core/dropbear/dropbear/CVE-2016-7409.patch new file mode 100644 index 0000000000..1475475b4d --- /dev/null +++ b/meta/recipes-core/dropbear/dropbear/CVE-2016-7409.patch @@ -0,0 +1,27 @@ + +# HG changeset patch +# User Matt Johnston +# Date 1468245085 -28800 +# Node ID 6a14b1f6dc04e70933c49ea335184e68c1deeb94 +# Parent 309e1c4a87682b6ca7d80b8555a1db416c3cb7ac +better TRACE of failed remote ident + +CVE: CVE-2016-7409 +Upstream-Status: Backport [backported from: +https://secure.ucc.asn.au/hg/dropbear/raw-rev/6a14b1f6dc04] + +Signed-off-by: Sona Sarmadi + +diff -r 309e1c4a8768 -r 6a14b1f6dc04 common-session.c +--- a/common-session.c Fri Mar 18 22:44:36 2016 +0800 ++++ b/common-session.c Mon Jul 11 21:51:25 2016 +0800 +@@ -361,7 +361,7 @@ + } + + if (!done) { +- TRACE(("err: %s for '%s'\n", strerror(errno), linebuf)) ++ TRACE(("error reading remote ident: %s\n", strerror(errno))) + ses.remoteclosed(); + } else { + /* linebuf is already null terminated */ + -- cgit 1.2.3-korg