Fix indefinite length encoding so EOC correctly updates
[openssl.git] / crypto / pkcs7 / pk7_mime.c
index 734643be2874f5e013356505c9dba5407c8c9fb4..431aff94f0b0173870bab38ccb5fce6ac88b350e 100644 (file)
@@ -1,9 +1,9 @@
 /* pk7_mime.c */
 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
- * project 1999.
+ * project.
  */
 /* ====================================================================
- * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1999-2003 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * parameter values. Quotes are stripped off
  */
 
-typedef struct {
-char *name;                            /* Name of line e.g. "content-type" */
-char *value;                           /* Value of line e.g. "text/plain" */
-STACK /* MIME_PARAM */ *params;                /* Zero or more parameters */
-} MIME_HEADER;
-
 typedef struct {
 char *param_name;                      /* Param name e.g. "micalg" */
 char *param_value;                     /* Param value e.g. "sha1" */
 } MIME_PARAM;
 
+DECLARE_STACK_OF(MIME_PARAM)
+IMPLEMENT_STACK_OF(MIME_PARAM)
+
+typedef struct {
+char *name;                            /* Name of line e.g. "content-type" */
+char *value;                           /* Value of line e.g. "text/plain" */
+STACK_OF(MIME_PARAM) *params;          /* Zero or more parameters */
+} MIME_HEADER;
+
+DECLARE_STACK_OF(MIME_HEADER)
+IMPLEMENT_STACK_OF(MIME_HEADER)
 
+static int pkcs7_output_data(BIO *bio, BIO *data, PKCS7 *p7, int flags);
 static int B64_write_PKCS7(BIO *bio, PKCS7 *p7);
 static PKCS7 *B64_read_PKCS7(BIO *bio);
 static char * strip_ends(char *name);
@@ -88,14 +94,16 @@ static char * strip_start(char *name);
 static char * strip_end(char *name);
 static MIME_HEADER *mime_hdr_new(char *name, char *value);
 static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value);
-static STACK *mime_parse_hdr(BIO *bio);
-static int mime_hdr_cmp(MIME_HEADER **a, MIME_HEADER **b);
-static int mime_param_cmp(MIME_PARAM **a, MIME_PARAM **b);
+static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio);
+static int mime_hdr_cmp(const MIME_HEADER * const *a,
+                       const MIME_HEADER * const *b);
+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 **ret);
+static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret);
 static int iscrlf(char c);
-static MIME_HEADER *mime_hdr_find(STACK *hdrs, char *name);
+static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name);
 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name);
 static void mime_hdr_free(MIME_HEADER *hdr);
 
@@ -143,7 +151,6 @@ static PKCS7 *B64_read_PKCS7(BIO *bio)
 
 int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
 {
-       char linebuf[MAX_SMLEN];
        char bound[33], c;
        int i;
        if((flags & PKCS7_DETACHED) && data) {
@@ -158,15 +165,15 @@ int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
                }
                bound[32] = 0;
                BIO_printf(bio, "MIME-Version: 1.0\n");
-               BIO_printf(bio, "Content-Type: multipart/signed ; ");
-               BIO_printf(bio, "protocol=\"application/x-pkcs7-signature\" ; ");
-               BIO_printf(bio, "micalg=sha1 ; boundary=\"----%s\"\n\n", bound);
+               BIO_printf(bio, "Content-Type: multipart/signed;");
+               BIO_printf(bio, " protocol=\"application/x-pkcs7-signature\";");
+               BIO_printf(bio, " micalg=sha1; boundary=\"----%s\"\n\n", bound);
                BIO_printf(bio, "This is an S/MIME signed message\n\n");
                /* Now write out the first part */
                BIO_printf(bio, "------%s\r\n", bound);
-               if(flags & PKCS7_TEXT) BIO_printf(bio, "Content-Type: text/plain\n\n");
-               while((i = BIO_read(data, linebuf, MAX_SMLEN)) > 0) 
-                                               BIO_write(bio, linebuf, i);
+
+               pkcs7_output_data(bio, data, p7, flags);
+
                BIO_printf(bio, "\n------%s\n", bound);
 
                /* Headers for signature */
@@ -188,6 +195,47 @@ int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
        return 1;
 }
 
+/* Handle output of PKCS#7 data */
+
+
+static int pkcs7_output_data(BIO *out, BIO *data, PKCS7 *p7, int flags)
+       {
+       BIO *tmpbio, *p7bio;
+
+       if (!(flags & PKCS7_STREAM))
+               {
+               SMIME_crlf_copy(data, out, flags);
+               return 1;
+               }
+
+       /* Partial sign operation */
+
+       /* Initialize sign operation */
+       p7bio = PKCS7_dataInit(p7, out);
+
+       /* Copy data across, computing digests etc */
+       SMIME_crlf_copy(data, p7bio, flags);
+
+       /* Must be detached */
+       PKCS7_set_detached(p7, 1);
+
+       /* Finalize signatures */
+       PKCS7_dataFinal(p7, p7bio);
+
+       /* Now remove any digests from output BIO */
+
+       while (1)
+               {
+               tmpbio = BIO_pop(p7bio);
+               if (tmpbio == out)
+                       break;
+               BIO_free(tmpbio);
+               }
+
+       return 1;
+
+       }
+
 /* SMIME reader: handle multipart/signed and opaque signing.
  * in multipart case the content is placed in a memory BIO
  * pointed to by "bcont". In opaque this is set to NULL
@@ -196,8 +244,8 @@ int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags)
 PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
 {
        BIO *p7in;
-       STACK *headers = NULL;
-       STACK *parts = NULL;
+       STACK_OF(MIME_HEADER) *headers = NULL;
+       STACK_OF(BIO) *parts = NULL;
        MIME_HEADER *hdr;
        MIME_PARAM *prm;
        PKCS7 *p7;
@@ -211,7 +259,7 @@ PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
        }
 
        if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
-               sk_pop_free(headers, mime_hdr_free);
+               sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_CONTENT_TYPE);
                return NULL;
        }
@@ -222,24 +270,24 @@ PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
                /* Split into two parts */
                prm = mime_param_find(hdr, "boundary");
                if(!prm || !prm->param_value) {
-                       sk_pop_free(headers, mime_hdr_free);
+                       sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                        PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BOUNDARY);
                        return NULL;
                }
                ret = multi_split(bio, prm->param_value, &parts);
-               sk_pop_free(headers, mime_hdr_free);
-               if(!ret || (sk_num(parts) != 2) ) {
+               sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
+               if(!ret || (sk_BIO_num(parts) != 2) ) {
                        PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BODY_FAILURE);
-                       sk_pop_free(parts, (stkfree)BIO_free);
+                       sk_BIO_pop_free(parts, BIO_vfree);
                        return NULL;
                }
 
                /* Parse the signature piece */
-               p7in = (BIO *)sk_value(parts, 1);
+               p7in = sk_BIO_value(parts, 1);
 
                if (!(headers = mime_parse_hdr(p7in))) {
                        PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_SIG_PARSE_ERROR);
-                       sk_pop_free(parts, (stkfree)BIO_free);
+                       sk_BIO_pop_free(parts, BIO_vfree);
                        return NULL;
                }
 
@@ -247,32 +295,32 @@ PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
 
                if(!(hdr = mime_hdr_find(headers, "content-type")) ||
                                                                 !hdr->value) {
-                       sk_pop_free(headers, mime_hdr_free);
+                       sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                        PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_SIG_CONTENT_TYPE);
                        return NULL;
                }
 
                if(strcmp(hdr->value, "application/x-pkcs7-signature") &&
                        strcmp(hdr->value, "application/pkcs7-signature")) {
-                       sk_pop_free(headers, mime_hdr_free);
+                       sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                        PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_SIG_INVALID_MIME_TYPE);
                        ERR_add_error_data(2, "type: ", hdr->value);
-                       sk_pop_free(parts, (stkfree)BIO_free);
+                       sk_BIO_pop_free(parts, BIO_vfree);
                        return NULL;
                }
-               sk_pop_free(headers, mime_hdr_free);
+               sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                /* Read in PKCS#7 */
                if(!(p7 = B64_read_PKCS7(p7in))) {
                        PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_PKCS7_SIG_PARSE_ERROR);
-                       sk_pop_free(parts, (stkfree)BIO_free);
+                       sk_BIO_pop_free(parts, BIO_vfree);
                        return NULL;
                }
 
                if(bcont) {
-                       *bcont = (BIO *)sk_value(parts, 0);
+                       *bcont = sk_BIO_value(parts, 0);
                        BIO_free(p7in);
-                       sk_free(parts);
-               } else sk_pop_free(parts, (stkfree)BIO_free);
+                       sk_BIO_free(parts);
+               } else sk_BIO_pop_free(parts, BIO_vfree);
                return p7;
        }
                
@@ -282,11 +330,11 @@ PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont)
            strcmp (hdr->value, "application/pkcs7-mime")) {
                PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_INVALID_MIME_TYPE);
                ERR_add_error_data(2, "type: ", hdr->value);
-               sk_pop_free(headers, mime_hdr_free);
+               sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                return NULL;
        }
 
-       sk_pop_free(headers, mime_hdr_free);
+       sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
        
        if(!(p7 = B64_read_PKCS7(bio))) {
                PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_PKCS7_PARSE_ERROR);
@@ -307,7 +355,8 @@ int SMIME_crlf_copy(BIO *in, BIO *out, int flags)
                                                BIO_write(out, linebuf, len);
                return 1;
        }
-       if(flags & PKCS7_TEXT) BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
+       if(flags & PKCS7_TEXT)
+               BIO_printf(out, "Content-Type: text/plain\r\n\r\n");
        while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) {
                eol = 0;
                while(iscrlf(linebuf[len - 1])) {
@@ -325,24 +374,25 @@ int SMIME_text(BIO *in, BIO *out)
 {
        char iobuf[4096];
        int len;
-       STACK *headers;
+       STACK_OF(MIME_HEADER) *headers;
        MIME_HEADER *hdr;
+
        if (!(headers = mime_parse_hdr(in))) {
                PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_PARSE_ERROR);
                return 0;
        }
        if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) {
                PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_NO_CONTENT_TYPE);
-               sk_pop_free(headers, mime_hdr_free);
+               sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                return 0;
        }
        if (strcmp (hdr->value, "text/plain")) {
                PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_INVALID_MIME_TYPE);
                ERR_add_error_data(2, "type: ", hdr->value);
-               sk_pop_free(headers, mime_hdr_free);
+               sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
                return 0;
        }
-       sk_pop_free(headers, mime_hdr_free);
+       sk_MIME_HEADER_pop_free(headers, mime_hdr_free);
        while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
                                                BIO_write(out, iobuf, len);
        return 1;
@@ -352,18 +402,19 @@ int SMIME_text(BIO *in, BIO *out)
  * canonical parts in a STACK of bios
  */
 
-static int multi_split(BIO *bio, char *bound, STACK **ret)
+static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret)
 {
        char linebuf[MAX_SMLEN];
        int len, blen;
        BIO *bpart = NULL;
-       STACK *parts;
+       STACK_OF(BIO) *parts;
        char state, part, first;
+
        blen = strlen(bound);
        part = 0;
        state = 0;
        first = 1;
-       parts = sk_new(NULL);
+       parts = sk_BIO_new_null();
        *ret = parts;
        while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
                state = mime_bound_check(linebuf, len, bound, blen);
@@ -371,12 +422,12 @@ static int multi_split(BIO *bio, char *bound, STACK **ret)
                        first = 1;
                        part++;
                } else if(state == 2) {
-                       sk_push(parts, (char *)bpart);
+                       sk_BIO_push(parts, bpart);
                        return 1;
                } else if(part) {
                        if(first) {
                                first = 0;
-                               if(bpart) sk_push(parts, (char *)bpart);
+                               if(bpart) sk_BIO_push(parts, bpart);
                                bpart = BIO_new(BIO_s_mem());
                                
                        } else BIO_write(bpart, "\r\n", 2);
@@ -405,15 +456,16 @@ static int iscrlf(char c)
 #define MIME_COMMENT   6
 
 
-static STACK *mime_parse_hdr(BIO *bio)
+static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio)
 {
        char *p, *q, c;
        char *ntmp;
        char linebuf[MAX_SMLEN];
        MIME_HEADER *mhdr = NULL;
-       STACK *headers;
+       STACK_OF(MIME_HEADER) *headers;
        int len, state, save_state = 0;
-       headers = sk_new(mime_hdr_cmp);
+
+       headers = sk_MIME_HEADER_new(mime_hdr_cmp);
        while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) {
        /* If whitespace at line start then continuation line */
        if(mhdr && isspace((unsigned char)linebuf[0])) state = MIME_NAME;
@@ -441,7 +493,7 @@ static STACK *mime_parse_hdr(BIO *bio)
                                mime_debug("Found End Value\n");
                                *p = 0;
                                mhdr = mime_hdr_new(ntmp, strip_ends(q));
-                               sk_push(headers, (char *)mhdr);
+                               sk_MIME_HEADER_push(headers, mhdr);
                                ntmp = NULL;
                                q = p + 1;
                                state = MIME_NAME;
@@ -493,7 +545,7 @@ static STACK *mime_parse_hdr(BIO *bio)
 
        if(state == MIME_TYPE) {
                mhdr = mime_hdr_new(ntmp, strip_ends(q));
-               sk_push(headers, (char *)mhdr);
+               sk_MIME_HEADER_push(headers, mhdr);
        } else if(state == MIME_VALUE)
                         mime_hdr_addparam(mhdr, ntmp, strip_ends(q));
        if(p == linebuf) break; /* Blank line means end of headers */
@@ -569,11 +621,11 @@ static MIME_HEADER *mime_hdr_new(char *name, char *value)
                        }
                }
        } else tmpval = NULL;
-       mhdr = (MIME_HEADER *) Malloc(sizeof(MIME_HEADER));
+       mhdr = (MIME_HEADER *) OPENSSL_malloc(sizeof(MIME_HEADER));
        if(!mhdr) return NULL;
        mhdr->name = tmpname;
        mhdr->value = tmpval;
-       if(!(mhdr->params = sk_new(mime_param_cmp))) return NULL;
+       if(!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) return NULL;
        return mhdr;
 }
                
@@ -598,34 +650,36 @@ static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value)
                if(!tmpval) return 0;
        } else tmpval = NULL;
        /* Parameter values are case sensitive so leave as is */
-       mparam = (MIME_PARAM *) Malloc(sizeof(MIME_PARAM));
+       mparam = (MIME_PARAM *) OPENSSL_malloc(sizeof(MIME_PARAM));
        if(!mparam) return 0;
        mparam->param_name = tmpname;
        mparam->param_value = tmpval;
-       sk_push(mhdr->params, (char *)mparam);
+       sk_MIME_PARAM_push(mhdr->params, mparam);
        return 1;
 }
 
-static int mime_hdr_cmp(MIME_HEADER **a, MIME_HEADER **b)
+static int mime_hdr_cmp(const MIME_HEADER * const *a,
+                       const MIME_HEADER * const *b)
 {
        return(strcmp((*a)->name, (*b)->name));
 }
 
-static int mime_param_cmp(MIME_PARAM **a, MIME_PARAM **b)
+static int mime_param_cmp(const MIME_PARAM * const *a,
+                       const MIME_PARAM * const *b)
 {
        return(strcmp((*a)->param_name, (*b)->param_name));
 }
 
 /* Find a header with a given name (if possible) */
 
-static MIME_HEADER *mime_hdr_find(STACK *hdrs, char *name)
+static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name)
 {
        MIME_HEADER htmp;
        int idx;
        htmp.name = name;
-       idx = sk_find(hdrs, (char *)&htmp);
+       idx = sk_MIME_HEADER_find(hdrs, &htmp);
        if(idx < 0) return NULL;
-       return (MIME_HEADER *)sk_value(hdrs, idx);
+       return sk_MIME_HEADER_value(hdrs, idx);
 }
 
 static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name)
@@ -633,24 +687,24 @@ static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name)
        MIME_PARAM param;
        int idx;
        param.param_name = name;
-       idx = sk_find(hdr->params, (char *)&param);
+       idx = sk_MIME_PARAM_find(hdr->params, &param);
        if(idx < 0) return NULL;
-       return (MIME_PARAM *)sk_value(hdr->params, idx);
+       return sk_MIME_PARAM_value(hdr->params, idx);
 }
 
 static void mime_hdr_free(MIME_HEADER *hdr)
 {
-       if(hdr->name) Free(hdr->name);
-       if(hdr->value) Free(hdr->value);
-       if(hdr->params) sk_pop_free(hdr->params, mime_param_free);
-       Free(hdr);
+       if(hdr->name) OPENSSL_free(hdr->name);
+       if(hdr->value) OPENSSL_free(hdr->value);
+       if(hdr->params) sk_MIME_PARAM_pop_free(hdr->params, mime_param_free);
+       OPENSSL_free(hdr);
 }
 
 static void mime_param_free(MIME_PARAM *param)
 {
-       if(param->param_name) Free(param->param_name);
-       if(param->param_value) Free(param->param_value);
-       Free(param);
+       if(param->param_name) OPENSSL_free(param->param_name);
+       if(param->param_value) OPENSSL_free(param->param_value);
+       OPENSSL_free(param);
 }
 
 /* Check for a multipart boundary. Returns: