This is an update of the patch made by Antti Tapio for 0.9.8a - ticket
#1261
Index: apps/smime.c
===================================================================
RCS file: /home/john/cvsroot/openssl/apps/smime.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- apps/smime.c 14 Oct 2011 11:17:40 -0000 1.1.1.1
+++ apps/smime.c 20 Oct 2011 07:16:06 -0000 1.1.1.1.2.1
@@ -78,7 +78,7 @@ static int smime_cb(int ok, X509_STORE_C
#define SMIME_ENCRYPT (1 | SMIME_OP)
#define SMIME_DECRYPT (2 | SMIME_IP)
#define SMIME_SIGN (3 | SMIME_OP | SMIME_SIGNERS)
-#define SMIME_VERIFY (4 | SMIME_IP)
+#define SMIME_VERIFY (4 | SMIME_IP | SMIME_OP)
#define SMIME_PK7OUT (5 | SMIME_IP | SMIME_OP)
#define SMIME_RESIGN (6 | SMIME_IP | SMIME_OP | SMIME_SIGNERS)
@@ -365,6 +365,23 @@ int MAIN(int argc, char **argv)
goto argerr;
contfile = *++args;
}
+ else if (!strcmp(*args, "-transenc") || !strcmp (*args, "-transferencoding"))
+ {
+ if (args[1])
+ {
+ if (!strcmp(args[1], "binary"))
+ flags |= SMIME_TRANSFER_ENCODING_BINARY;
+ else if (!strcmp(args[1], "base64"))
+ ;
+ else {
+ BIO_printf(bio_err, "Supported transfer encodings are base64 and binary\n");
+ badarg = 1;
+ }
+ args++;
+ }
+ else
+ badarg = 1;
+ }
else if (args_verify(&args, NULL, &badarg, bio_err, &vpm))
continue;
else if ((cipher = EVP_get_cipherbyname(*args + 1)) == NULL)
@@ -488,6 +505,7 @@ int MAIN(int argc, char **argv)
BIO_printf(bio_err, "-rand file%cfile%c...\n", LIST_SEPARATOR_CHAR, LIST_SEPARATOR_CHAR);
BIO_printf(bio_err, " load the file (or the files in the directory) into\n");
BIO_printf(bio_err, " the random number generator\n");
+ BIO_printf(bio_err, "-transenc enc transfer encoding to use (base64 or binary)\n");
BIO_printf (bio_err, "cert.pem recipient certificate(s) for encryption\n");
goto end;
}
Index: crypto/asn1/asn1.h
===================================================================
RCS file: /home/john/cvsroot/openssl/crypto/asn1/asn1.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- crypto/asn1/asn1.h 14 Oct 2011 11:17:40 -0000 1.1.1.1
+++ crypto/asn1/asn1.h 15 Oct 2011 09:36:51 -0000 1.1.1.1.2.1
@@ -161,6 +161,7 @@ extern "C" {
#define SMIME_OLDMIME 0x400
#define SMIME_CRLFEOL 0x800
#define SMIME_STREAM 0x1000
+#define SMIME_TRANSFER_ENCODING_BINARY 0x2000
struct X509_algor_st;
DECLARE_STACK_OF(X509_ALGOR)
@@ -1222,6 +1223,8 @@ void ERR_load_ASN1_strings(void);
#define ASN1_F_ASN1_VERIFY 137
#define ASN1_F_B64_READ_ASN1 209
#define ASN1_F_B64_WRITE_ASN1 210
+#define ASN1_F_BINARY_READ_ASN1 219
+#define ASN1_F_BINARY_WRITE_ASN1 220
#define ASN1_F_BIO_NEW_NDEF 208
#define ASN1_F_BITSTR_CB 180
#define ASN1_F_BN_TO_ASN1_ENUMERATED 138
@@ -1335,6 +1338,7 @@ void ERR_load_ASN1_strings(void);
#define ASN1_R_INVALID_OBJECT_ENCODING 216
#define ASN1_R_INVALID_SEPARATOR 131
#define ASN1_R_INVALID_TIME_FORMAT 132
+#define ASN1_R_INVALID_TRANSFER_ENCODING 217
#define ASN1_R_INVALID_UNIVERSALSTRING_LENGTH 133
#define ASN1_R_INVALID_UTF8STRING 134
#define ASN1_R_IV_TOO_LARGE 135
Index: crypto/asn1/asn1_err.c
===================================================================
RCS file: /home/john/cvsroot/openssl/crypto/asn1/asn1_err.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- crypto/asn1/asn1_err.c 14 Oct 2011 11:17:40 -0000 1.1.1.1
+++ crypto/asn1/asn1_err.c 15 Oct 2011 09:36:51 -0000 1.1.1.1.2.1
@@ -135,6 +135,8 @@ static ERR_STRING_DATA ASN1_str_functs[]
{ERR_FUNC(ASN1_F_ASN1_VERIFY), "ASN1_verify"},
{ERR_FUNC(ASN1_F_B64_READ_ASN1), "B64_READ_ASN1"},
{ERR_FUNC(ASN1_F_B64_WRITE_ASN1), "B64_WRITE_ASN1"},
+{ERR_FUNC(ASN1_F_BINARY_READ_ASN1), "BINARY_READ_ASN1"},
+{ERR_FUNC(ASN1_F_BINARY_WRITE_ASN1), "BINARY_WRITE_ASN1"},
{ERR_FUNC(ASN1_F_BIO_NEW_NDEF), "BIO_new_NDEF"},
{ERR_FUNC(ASN1_F_BITSTR_CB), "BITSTR_CB"},
{ERR_FUNC(ASN1_F_BN_TO_ASN1_ENUMERATED), "BN_to_ASN1_ENUMERATED"},
Index: crypto/asn1/asn_mime.c
===================================================================
RCS file: /home/john/cvsroot/openssl/crypto/asn1/asn_mime.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.2
diff -u -p -r1.1.1.1 -r1.1.1.1.2.2
--- crypto/asn1/asn_mime.c 14 Oct 2011 11:17:40 -0000 1.1.1.1
+++ crypto/asn1/asn_mime.c 20 Oct 2011 07:16:07 -0000 1.1.1.1.2.2
@@ -100,7 +100,6 @@ static int mime_hdr_cmp(const MIME_HEADE
static int mime_param_cmp(const MIME_PARAM * const *a,
const MIME_PARAM * const *b);
static void mime_param_free(MIME_PARAM *param);
-static int mime_bound_check(char *line, int linelen, char *bound, int blen);
static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
static int strip_eol(char *linebuf, int *plen);
static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
@@ -143,7 +142,7 @@ int i2d_ASN1_bio_stream(BIO *out, ASN1_V
return 1;
}
-/* Base 64 read and write of ASN1 structure */
+/* Base 64 and binary read and write of ASN1 structure */
static int B64_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
const ASN1_ITEM *it)
@@ -166,6 +165,15 @@ static int B64_write_ASN1(BIO *out, ASN1
return r;
}
+static int BINARY_write_ASN1(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
+ const ASN1_ITEM *it)
+ {
+ int r;
+ r = i2d_ASN1_bio_stream(out, val, in, flags, it);
+ (void)BIO_flush(out);
+ return r;
+ }
+
/* Streaming ASN1 PEM write */
int PEM_write_bio_ASN1_stream(BIO *out, ASN1_VALUE *val, BIO *in, int flags,
@@ -197,6 +205,16 @@ static ASN1_VALUE *b64_read_asn1(BIO *bi
return val;
}
+static ASN1_VALUE *binary_read_asn1(BIO *bio, const ASN1_ITEM *it)
+{
+ ASN1_VALUE *val;
+ val = ASN1_item_d2i_bio(it, bio, NULL);
+ if(!val)
+ ASN1err(ASN1_F_BINARY_READ_ASN1,ASN1_R_DECODE_ERROR);
+ (void)BIO_flush(bio);
+ return val;
+}
+
/* Generate the MIME "micalg" parameter from RFC3851, RFC4490 */
static int asn1_write_micalg(BIO *out, STACK_OF(X509_ALGOR) *mdalgs)
@@ -323,12 +341,20 @@ int SMIME_write_ASN1(BIO *bio, ASN1_VALU
BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix);
BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol);
- BIO_printf(bio, "Content-Transfer-Encoding: base64%s",
+ BIO_printf(bio, "Content-Transfer-Encoding: %s%s",
+ (flags&SMIME_TRANSFER_ENCODING_BINARY) ? "binary" : "base64",
mime_eol);
BIO_printf(bio, "Content-Disposition: attachment;");
BIO_printf(bio, " filename=\"smime.p7s\"%s%s",
mime_eol, mime_eol);
- B64_write_ASN1(bio, val, NULL, 0, it);
+
+ if (flags & SMIME_TRANSFER_ENCODING_BINARY) {
+ BINARY_write_ASN1(bio, val, NULL, 0, it);
+ BIO_printf(bio, "%s", mime_eol);
+ }
+ else
+ B64_write_ASN1(bio, val, NULL, 0, it);
+
BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound,
mime_eol, mime_eol);
return 1;
@@ -360,10 +386,18 @@ int SMIME_write_ASN1(BIO *bio, ASN1_VALU
if (msg_type)
BIO_printf(bio, " smime-type=%s;", msg_type);
BIO_printf(bio, " name=\"%s\"%s", cname, mime_eol);
- BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s",
+ BIO_printf(bio, "Content-Transfer-Encoding: %s%s%s",
+ (flags&SMIME_TRANSFER_ENCODING_BINARY) ? "binary" : "base64",
mime_eol, mime_eol);
- if (!B64_write_ASN1(bio, val, data, flags, it))
- return 0;
+ if (flags & SMIME_TRANSFER_ENCODING_BINARY) {
+ if (!BINARY_write_ASN1(bio, val, data, flags, it))
+ return 0;
+ }
+ else {
+ if (!B64_write_ASN1(bio, val, data, flags, it))
+ return 0;
+ }
+
BIO_printf(bio, "%s", mime_eol);
return 1;
}
@@ -434,6 +468,7 @@ ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BI
MIME_PARAM *prm;
ASN1_VALUE *val;
int ret;
+ int base64;
if(bcont) *bcont = NULL;
@@ -492,9 +527,29 @@ ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BI
sk_BIO_pop_free(parts, BIO_vfree);
return NULL;
}
+
+ /* Base64 or binary? Default to binary. This is
+ inconsistent with old code, but needed for AS2 */
+
+ base64 = 0;
+ if ((hdr = mime_hdr_find(headers, "content-transfer-encoding"))) {
+ if (hdr->value && strcmp (hdr->value, "base64") == 0)
+ base64 = 1;
+ else if (!hdr->value || strcmp (hdr->value, "binary") != 0) {
+ sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+ ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_INVALID_TRANSFER_ENCODING);
+ sk_BIO_pop_free (parts, BIO_vfree);
+ return NULL;
+ }
+ }
+
sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
/* Read in ASN1 */
- if(!(val = b64_read_asn1(asnin, it))) {
+
+ if(!(val = base64 ?
+ b64_read_asn1(asnin, it) :
+ binary_read_asn1(asnin, it)))
+ {
ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_ASN1_SIG_PARSE_ERROR);
sk_BIO_pop_free(parts, BIO_vfree);
return NULL;
@@ -518,9 +573,25 @@ ASN1_VALUE *SMIME_read_ASN1(BIO *bio, BI
return NULL;
}
+ /* Base64 or binary? Default to binary. This is
+ inconsistent with old code, but needed for AS2 */
+
+ base64 = 0;
+ if ((hdr = mime_hdr_find(headers, "content-transfer-encoding"))) {
+ if (hdr->value && strcmp (hdr->value, "base64") == 0)
+ base64 = 1;
+ else if (!hdr->value || strcmp (hdr->value, "binary") != 0) {
+ sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+ ASN1err(ASN1_F_SMIME_READ_ASN1,ASN1_R_INVALID_TRANSFER_ENCODING);
+ return NULL;
+ }
+ }
+
sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
- if(!(val = b64_read_asn1(bio, it))) {
+ if(!(val = base64 ? b64_read_asn1(bio, it) :
+ binary_read_asn1(bio, it)))
+ {
ASN1err(ASN1_F_SMIME_READ_ASN1, ASN1_R_ASN1_PARSE_ERROR);
return NULL;
}
@@ -597,48 +668,163 @@ int SMIME_text(BIO *in, BIO *out)
return 1;
}
-/* Split a multipart/XXX message body into component parts: result is
- * canonical parts in a STACK of bios
+/*
+ * Returns >0 if the given string starts with a linebreak.
+ * The return code is the index of the first character
+ * after the line break.
+ */
+static int starts_with_linebreak(const char *s) {
+ const char *p;
+ p = s;
+ if(*p == '\x0d') {
+ ++p;
+ if(*p == '\x0a')
+ ++p;
+ } else if(*p == '\x0a') {
+ ++p;
+ }
+ return (p - s);
+}
+
+/*
+ * Finds a boundary in a buffer and returns the length of the boundary (this can
+ * vary depending on the kind of linebreaks), the starting index of the boundary
+ * and a flag telling if it is an ending boundary.
+ * Parameters:
+ * buf - buffer to search in
+ * buflen - length of buf
+ * bound - the boundary string to match
+ * blen - length of bound
+ * boundstart - returns the index of the start of the boundary
+ * ending - returns 1 if the found boundary is an ending boundary,
+ * i.e. if it ends with a "--"
+ *
+ * Returns:
+ * 0 - No boundary found
+ * >0 - Boundary found and the return value is the length of boundary,
+ * which can be skipped to proceed to the next MIME part.
*/
+static int find_boundary_start(const char *buf, int buflen, const char *bound, int blen, int *boundstart, int *ending) {
+ int e = -1;
+ int i, e2;
+ int is;
+ const char *p;
-static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
-{
- char linebuf[MAX_SMLEN];
- int len, blen;
- int eol = 0, next_eol = 0;
+ if(buflen == -1) return 0;
+ if(blen == -1) blen = strlen(bound);
+
+
+ /* Quickly eliminate if line length too short */
+ if((blen + 3) > buflen)
+ return 0;
+
+ *ending = 0;
+ is = buflen - (blen + 3);
+ for(i = 0; i <= is; i++) {
+ *boundstart = i;
+ p = buf + i;
+ e = i;
+ if((e = starts_with_linebreak(p)) > 0) {
+ if(!strncmp(p + e, "--", 2)) {
+ e += 2;
+ if((e + blen) < buflen && !strncmp(p + e, bound, blen))
+ e += blen;
+ else
+ e = 0;
+ } else
+ e = 0;
+ }
+
+ if(e > 0) {
+ /* Look what is behind */
+ if((e + 2) < buflen && !strncmp(p + e, "--", 2)) {
+ e += 2;
+ *ending = 1;
+ }
+
+ /* Strip away the trailing linebreaks, for test issues */
+ if(e > 0 && (e + 2) < buflen && (e2 = starts_with_linebreak(p + e)) > 0) {
+ e += e2;
+ }
+ break;
+ }
+ }
+
+ return e;
+}
+
+/* Split a multipart/XXX message body into component parts: result is
+ * parts in a STACK of bios. Is considered to be able to read also
+ * binary parts.
+ * Tries to split after the following rules (EBNF):
+ * { LineBreak }, LineBreak, Boundary, LineBreak, Data,
+ * ( LineBreak, Boundary, [ "--" ] | [ LineBreak | EOF ] ) |
+ * EOF
+ * LineBreak := ( "\x0d", "\x0a" ) | "\x0a" | "\x0d"
+ */
+static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret) {
+ int len, len2, boundlen;
+ char rbuf[MAX_SMLEN];
+ char *buf = rbuf;
BIO *bpart = NULL;
STACK_OF(BIO) *parts;
- char state, part, first;
-
- blen = strlen(bound);
- part = 0;
- state = 0;
- first = 1;
+ int boundary_found, boundary_start, boundary_ending, maxboundlen;
+
+ boundlen = strlen(bound);
+ maxboundlen = 2 + 2 + boundlen + 2 + 2;
parts = sk_BIO_new_null();
*ret = parts;
- while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
- state = mime_bound_check(linebuf, len, bound, blen);
- if(state == 1) {
- first = 1;
- part++;
- } else if(state == 2) {
- sk_BIO_push(parts, bpart);
- return 1;
- } else if(part) {
- /* Strip CR+LF from linebuf */
- next_eol = strip_eol(linebuf, &len);
- if(first) {
- first = 0;
- if(bpart) sk_BIO_push(parts, bpart);
+ len = 0;
+
+ while((len = (len > 0) ? len : BIO_read(bio, (buf = rbuf), MAX_SMLEN)) > 0) {
+ /* If there are not enough bytes in the buffer, to compare with the
+ * boundary, fill the buffer up
+ */
+ if(len <= maxboundlen) {
+ memmove(rbuf, buf, len); buf = rbuf;
+ len2 = BIO_read(bio, buf + len, MAX_SMLEN - len);
+ if(len2 >= (maxboundlen - len))
+ len += len2;
+ else {
+ /* Stream ended here, write the characters away and return */
+ BIO_write(bpart, buf, (len + (len2 > 0) ? len2 : 0));
+ if(bpart)
+ sk_BIO_push(parts, bpart);
+ break;
+ }
+ }
+
+ if((boundary_found = find_boundary_start(buf, len, bound, boundlen, &boundary_start, &boundary_ending)) > 0) {
+ /* If there is something before the boundary, write it to the part */
+ if(boundary_start > 0 && bpart)
+ BIO_write(bpart, buf, boundary_start);
+ buf += boundary_start; len -= boundary_start;
+ memmove(rbuf, buf, len); buf = rbuf;
+ len2 = BIO_read(bio, buf + len, MAX_SMLEN - len);
+ len += (len2) ? len2 : 0;
+
+ boundary_found = find_boundary_start(buf, len, bound, boundlen, &boundary_start, &boundary_ending);
+ /* If boundary is not the last one, create a new part */
+ if(!boundary_ending) {
+ if(bpart)
+ sk_BIO_push(parts, bpart);
bpart = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(bpart, 0);
- } else if (eol)
- BIO_write(bpart, "\r\n", 2);
- eol = next_eol;
- if (len)
- BIO_write(bpart, linebuf, len);
+ } else {
+ /* End is reached */
+ if(bpart)
+ sk_BIO_push(parts, bpart);
+ return 1;
+ }
+ buf += (boundary_start + boundary_found); len -= (boundary_start + boundary_found);
+ } else {
+ if(bpart)
+ BIO_write(bpart, buf, len - maxboundlen);
+ buf += (len - maxboundlen); len = maxboundlen;
+ /* ELSE: Just forget it, no MIME part started */
}
}
+
return 0;
}
@@ -904,25 +1090,6 @@ static void mime_param_free(MIME_PARAM *
OPENSSL_free(param);
}
-/* Check for a multipart boundary. Returns:
- * 0 : no boundary
- * 1 : part boundary
- * 2 : final boundary
- */
-static int mime_bound_check(char *line, int linelen, char *bound, int blen)
-{
- if(linelen == -1) linelen = strlen(line);
- if(blen == -1) blen = strlen(bound);
- /* Quickly eliminate if line length too short */
- if(blen + 2 > linelen) return 0;
- /* Check for part boundary */
- if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) {
- if(!strncmp(line + blen + 2, "--", 2)) return 2;
- else return 1;
- }
- return 0;
-}
-
static int strip_eol(char *linebuf, int *plen)
{
int len = *plen;