LCOV - code coverage report
Current view: top level - zxid - smimemime.c (source / functions) Hit Total Coverage
Test: ZXID Code Coverage Lines: 76 155 49.0 %
Date: 2010-12-19 Functions: 8 11 72.7 %
Branches: 47 140 33.6 %

           Branch data     Line data    Source code
       1                 :            : /* smimemime.c  -  Utility functions for performing MIME assembly and parsing
       2                 :            :  *
       3                 :            :  * Copyright (c) 1999 Sampo Kellomaki <sampo@iki.fi>, All Rights Reserved.
       4                 :            :  * License: This software may be distributed under the same license
       5                 :            :  *          terms as openssl (i.e. free, but mandatory attribution).
       6                 :            :  *          See file LICENSE for details.
       7                 :            :  *
       8                 :            :  * 11.9.1999, Created. --Sampo
       9                 :            :  * 13.9.1999, 0.1 released. Now adding verify. --Sampo
      10                 :            :  * 1.10.1999, improved error handling, fixed decrypt --Sampo
      11                 :            :  * 6.10.1999, separated from smimeutil.c --Sampo
      12                 :            :  * 9.10.1999, reviewed for double frees --Sampo
      13                 :            :  *
      14                 :            :  * This module has been developed to support a Lingo XTRA that is supposed
      15                 :            :  * to provide crypto functionality. It may, however, be useful for other
      16                 :            :  * purposes as well.
      17                 :            :  *
      18                 :            :  * This is a very simple S/MIME library. For example the multipart
      19                 :            :  * boundary separators are hard coded and no effort is made to verify
      20                 :            :  * that mime entities are in their canonical form before signing (the
      21                 :            :  * caller should make sure they are, canonical form means using CRLF
      22                 :            :  * as line termination, among other things). Also the multipart functionality
      23                 :            :  * only understands up to 3 attachments. For many tasks this is enough,
      24                 :            :  * but if its not, feel free to write more generic utilities.
      25                 :            :  *
      26                 :            :  * Memory management: most routines malloc the results. Freeing them is
      27                 :            :  * application's responsibility. I use libc malloc, but if in doubt
      28                 :            :  * it might be safer to just leak the memory (i.e. don't ever free it).
      29                 :            :  * This library works entirely in memory, so maximum memory consumption
      30                 :            :  * might be more than twice the total size of all files to be encrypted.
      31                 :            :  */
      32                 :            : 
      33                 :            : #include "platform.h"
      34                 :            : 
      35                 :            : #include <stdio.h>
      36                 :            : #include <string.h>
      37                 :            : #include <time.h>
      38                 :            : 
      39                 :            : #if defined(macintosh) || defined(__MWERKS__)
      40                 :            : #include "macglue.h"
      41                 :            : #endif
      42                 :            : 
      43                 :            : #include <openssl/crypto.h>
      44                 :            : #include <openssl/buffer.h>
      45                 :            : #include <openssl/bio.h>
      46                 :            : #include <openssl/x509.h>
      47                 :            : #include <openssl/pem.h>
      48                 :            : #include <openssl/err.h>
      49                 :            : 
      50                 :            : #define SMIME_INTERNALS  /* we want also our internal helper functions */
      51                 :            : #include "smimeutil.h"
      52                 :            : 
      53                 :            : #include "logprint.h"
      54                 :            : 
      55                 :            : /* Called by:  clear_sign, encrypt1, sign */
      56                 :            : char*
      57                 :          3 : cut_pem_markers_off(char* b, int n, char* algo) {
      58                 :          3 :   int algolen = strlen(algo);
      59         [ -  + ]:          3 :   if (!b) return NULL;
      60                 :          3 :   b[n-6-algolen-4-5] = '\0'; /* cut off `-----END PKCS7-----\n' */
      61                 :          3 :   b+=5+6+algolen+6;          /* skip `-----BEGIN PKCS7-----\n' */
      62                 :          3 :   return b;  /* WARNING: returned value can not be freed */
      63                 :            : }
      64                 :            : 
      65                 :            : /* As an additional goodie, this inserts possibly missing newline
      66                 :            :  * to the end of pem entity.
      67                 :            :  */
      68                 :            : 
      69                 :            : /* Called by:  get_pkcs7_from_pem */
      70                 :            : char*
      71                 :          3 : wrap_in_pem_markers(const char* b, char* algo) {
      72                 :            :   char* bb;
      73                 :            :   int n;
      74                 :          3 :   int algolen = strlen(algo);  
      75                 :          3 :   n = strlen(b);
      76         [ -  + ]:          3 :   if (!(bb = (char*)OPENSSL_malloc(5+6+algolen+6+ n +5+4+algolen+6 +1 +1)))
      77                 :          0 :     GOTO_ERR("no memory?");
      78                 :          3 :   strcpy(bb, "-----BEGIN ");
      79                 :          3 :   strcat(bb, algo);
      80                 :          3 :   strcat(bb, "-----\n");
      81                 :          3 :   strcat(bb, b);
      82   [ +  +  +  - ]:          3 :   if (b[n-1] != '\012' && b[n-1] != '\015')  /* supply \n if missing */
      83                 :          2 :     strcat(bb, "\n");
      84                 :          3 :   strcat(bb, "-----END ");
      85                 :          3 :   strcat(bb, algo);
      86                 :          3 :   strcat(bb, "-----\n");
      87                 :          3 :   n = strlen(bb);
      88                 :          3 :   return bb;
      89                 :          0 : err:
      90                 :          0 :   return NULL;
      91                 :            : }
      92                 :            : 
      93                 :            : /* reallocing every time is not exactly the most efficient way, but it
      94                 :            :  * is simple and it works */
      95                 :            : 
      96                 :            : /* Called by:  attach x9, encrypt1, get_req_attr x3, mime_base64_entity x3, mime_mk_multipart x3, mime_raw_entity x3, sign, smime_mk_multipart_signed x4 */
      97                 :            : char*
      98                 :            : concat(char* b, const char* s)
      99                 :         45 : {
     100         [ -  + ]:         45 :   if (!(b = (char*)OPENSSL_realloc(b, strlen(b)+strlen(s)+1))) GOTO_ERR("no memory?");
     101                 :         45 :   strcat(b,s);
     102                 :         45 :   return b;
     103                 :          0 : err:
     104                 :          0 :   return NULL;
     105                 :            : }
     106                 :            : 
     107                 :            : /* Called by:  get_req_attr */
     108                 :            : char*
     109                 :            : concatmem(char* b, const char* s, int len)
     110                 :          0 : {
     111                 :          0 :   int lb = strlen(b);
     112         [ #  # ]:          0 :   if (!(b = (char*)OPENSSL_realloc(b, lb+len+1))) GOTO_ERR("no memory?");
     113                 :          0 :   memcpy(b+lb, s, len);
     114                 :          0 :   b[lb+len] = '\0';
     115                 :          0 :   return b;
     116                 :          0 : err:
     117                 :          0 :   return NULL;
     118                 :            : }
     119                 :            : 
     120                 :            : /* Arrange all headers for binary attachment */
     121                 :            : 
     122                 :            : /* Called by:  mime_mk_multipart x3 */
     123                 :            : static char*
     124                 :            : attach(char* b,
     125                 :            :        const char* data,
     126                 :            :        int len,
     127                 :            :        const char* type,
     128                 :            :        const char* name)
     129                 :          3 : {
     130                 :            :   int n;
     131                 :            :   char* b64;
     132                 :            :   
     133         [ +  + ]:          3 :   if (!type) return b;  /* type==NULL */
     134         [ -  + ]:          1 :   if (!*type) return b; /* type=="" */
     135         [ -  + ]:          1 :   if (!data) return b;
     136         [ -  + ]:          1 :   if (!name) return b;
     137                 :            :   
     138                 :          1 :   n = smime_base64(1, data, len, &b64);
     139         [ -  + ]:          1 :   if (!b64) return b;
     140                 :            :   
     141         [ +  - ]:          1 :   if (!(b = concat(b, CRLF "Content-type: "))) goto err;
     142         [ +  - ]:          1 :   if (!(b = concat(b, type))) goto err;
     143         [ +  - ]:          1 :   if (!(b = concat(b, "; name=\""))) goto err;
     144         [ +  - ]:          1 :   if (!(b = concat(b, name))) goto err;
     145         [ +  - ]:          1 :   if (!(b = concat(b, "\"" CRLF
     146                 :            :                    "Content-transfer-encoding: base64" CRLF
     147                 :            :                    "Content-disposition: inline; filename=\"")))
     148                 :          0 :     goto err;
     149         [ +  - ]:          1 :   if (!(b = concat(b, name))) goto err;
     150         [ +  - ]:          1 :   if (!(b = concat(b, "\"" CRLF CRLF))) goto err;
     151         [ +  - ]:          1 :   if (!(b = concat(b, b64))) goto err;
     152         [ +  - ]:          1 :   if (!(b = concat(b, CRLF "--" SEP))) goto err;
     153                 :          1 :   return b;
     154                 :          0 : err:
     155                 :          0 :   return NULL;
     156                 :            : }
     157                 :            : 
     158                 :            : /* ======= M I M E   M U L T I P A R T   M A N I P U L A T I O N ======= */
     159                 :            : 
     160                 :            : /* Create MIME multipart/mixed entity containing some text and
     161                 :            :  * then up to 3 attachmets. All attachments are base64 encoded so they
     162                 :            :  * can be binary, if needed. Text itself is assumed 8bit.
     163                 :            :  *
     164                 :            :  * Note: In MIME multiparts the CRLF before --separator is considered
     165                 :            :  * part of the separator. If data ends in CRLF, an empty line will
     166                 :            :  * appear.
     167                 :            :  */
     168                 :            : 
     169                 :            : /*
     170                 :            : Content-type: multipart/mixed; boundary=separator_42
     171                 :            : 
     172                 :            : --separator_42
     173                 :            : Content-type: text/plain
     174                 :            : Content-transfer-encoding: 8bit
     175                 :            : 
     176                 :            : First part is text.
     177                 :            : --separator_42
     178                 :            : Content-type: image/gif; name="foo.gif"
     179                 :            : Content-transfer-encoding: base64
     180                 :            : Content-disposition: attachment; filename="foo.gif"
     181                 :            : 
     182                 :            : AQW232ASA232NFKDJFD==
     183                 :            : --separator_42--
     184                 :            :  */
     185                 :            : 
     186                 :            : /* Called by:  mk_multipart */
     187                 :            : char*
     188                 :            : mime_mk_multipart(const char* text,
     189                 :            :           const char* file1, int len1, const char* type1, const char* name1,
     190                 :            :           const char* file2, int len2, const char* type2, const char* name2,
     191                 :            :           const char* file3, int len3, const char* type3, const char* name3)
     192                 :          1 : {
     193                 :            :   char* b;
     194                 :            :   
     195                 :            :   /* Concatenate all components into one message. This type of stuff
     196                 :            :    * is sooo ugly in C. I'm missing perl. */
     197                 :            :   
     198         [ -  + ]:          1 :   if (!(b = strdup("Content-type: multipart/mixed; boundary=" SEP CRLF
     199                 :            :                    CRLF
     200                 :            :                    "--" SEP CRLF
     201                 :            :                    "Content-type: text/plain" CRLF
     202                 :            :                    "Content-transfer-encoding: 8bit" CRLF
     203                 :          0 :                    CRLF))) GOTO_ERR("no memory?");
     204         [ +  - ]:          1 :   if (!(b = concat(b, text))) goto err;
     205         [ +  - ]:          1 :   if (!(b = concat(b, CRLF "--" SEP))) goto err;
     206                 :            : 
     207         [ +  - ]:          1 :   if (!(b = attach(b, file1, len1, type1, name1))) goto err;
     208         [ +  - ]:          1 :   if (!(b = attach(b, file2, len2, type2, name2))) goto err;
     209         [ +  - ]:          1 :   if (!(b = attach(b, file3, len3, type3, name3))) goto err;
     210                 :            :   
     211         [ +  - ]:          1 :   if (!(b = concat(b, "--" CRLF))) goto err;
     212                 :          1 :   return b;
     213                 :          0 : err:
     214                 :          0 :   return NULL;
     215                 :            : }
     216                 :            : 
     217                 :            : /* Called by:  clear_sign */
     218                 :            : char*  /* returns smime encoded clear sig blob, or NULL if error */
     219                 :            : smime_mk_multipart_signed(const char* mime_entity, const char* sig_entity)
     220                 :          0 : {
     221                 :            :   char* b;
     222                 :            :   
     223         [ #  # ]:          0 :   if (!(b = strdup("Content-type: multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=sha1; boundary=" SIG CRLF
     224                 :            :                    CRLF
     225                 :          0 :                    "--" SIG CRLF))) GOTO_ERR("no memory?");
     226         [ #  # ]:          0 :   if (!(b = concat(b, mime_entity))) goto err;
     227         [ #  # ]:          0 :   if (!(b = concat(b, CRLF "--" SIG CRLF
     228                 :            :    "Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"" CRLF
     229                 :            :    "Content-Transfer-Encoding: base64" CRLF
     230                 :            :    "Content-Disposition: attachment; filename=\"smime.p7s\"" CRLF CRLF)))
     231                 :          0 :     goto err;
     232         [ #  # ]:          0 :   if (!(b = concat(b, sig_entity))) goto err;
     233         [ #  # ]:          0 :   if (!(b = concat(b, CRLF "--" SIG "--" CRLF))) goto err;
     234                 :          0 :   return b;
     235                 :            : 
     236                 :          0 : err:
     237                 :          0 :   return NULL;
     238                 :            : }
     239                 :            : 
     240                 :            : /* Finds boundary marker from `Content-type: multipart/...; boundary=sep'
     241                 :            :  * header and splits the multiparts into array parts. Lengths of each
     242                 :            :  * part go to lengths array. If parts is NULL, just returns the number
     243                 :            :  * of parts found. Memory for each part is obtained from OPENSSL_malloc. Only
     244                 :            :  * one level of multipart encoding is interpretted, i.e. if one of the
     245                 :            :  * contained parts happens to be a multipart, it needs to be separately
     246                 :            :  * interpretted on a second pass.
     247                 :            :  *
     248                 :            :  * *** WARNING: this is not totally robust and mime compliant, improvements
     249                 :            :  *              welcome. --Sampo
     250                 :            :  */
     251                 :            : 
     252                 :            : int  /* returns number of parts, -1 on error */
     253                 :            : mime_split_multipart(const char* entity,
     254                 :            :                      int max_parts,  /* how big following arrays are */
     255                 :            :                      char* parts[],  /* NULL --> just count parts */
     256                 :            :                      int lengths[])
     257                 :          0 : {
     258                 :            :   char separator[256];
     259                 :          0 :   int nparts = -1;
     260                 :            :   int sep_len, len;
     261                 :            :   char* p;
     262                 :            :   char* pp;
     263                 :            :   char* ppp;
     264                 :          0 :   char* start = NULL;  /* start of current sub entity */
     265                 :            :   
     266   [ #  #  #  #  :          0 :   if (!entity || (parts && !lengths)) GOTO_ERR("NULL arg(s)");
                   #  # ]
     267                 :            : 
     268         [ #  # ]:          0 :   if (!(p = strstr(entity, "Content-type: multipart/")))
     269                 :          0 :     GOTO_ERR("16 No `Content-type: multipart/...' header found");
     270                 :            :   
     271         [ #  # ]:          0 :   if (!(p = strstr(p, "boundary=")))
     272                 :          0 :     GOTO_ERR("16 Badly formed multipart header. Didn't find `boundary='.");
     273                 :            : 
     274                 :          0 :   p+=9; /* strlen("boundary=") */
     275                 :          0 :   sep_len = strcspn(p, "\015\012 ;");
     276         [ #  # ]:          0 :   if (sep_len <= 0) GOTO_ERR("16 No boundary separator?");
     277         [ #  # ]:          0 :   if (sep_len >= (int)sizeof(separator))
     278                 :          0 :     GOTO_ERR("16 Too long boundary separator. Only 255 chars allowed.");
     279                 :            :   
     280                 :          0 :   separator[0] = separator[1] = '-';
     281                 :          0 :   memcpy(separator+2, p, sep_len);
     282                 :          0 :   sep_len+=2;
     283                 :          0 :   separator[sep_len] = '\0';
     284                 :          0 :   p+=sep_len;
     285                 :            :   
     286         [ #  # ]:          0 :   while ((p = strstr(p, separator))) {
     287                 :            : 
     288                 :          0 :     ppp = pp = p;
     289                 :          0 :     p+=sep_len;
     290   [ #  #  #  #  :          0 :     if (*p != '\015' && *p != '\012' && *p != '-')
                   #  # ]
     291                 :          0 :       continue;   /* False positive: separator appeared as line prefix */
     292                 :            : 
     293         [ #  # ]:          0 :     if (pp[-1] == '\012') pp--;  /* walk back and eat the CRLF */
     294         [ #  # ]:          0 :     if (pp[-1] == '\015') pp--;
     295         [ #  # ]:          0 :     if (pp == ppp)
     296                 :          0 :       continue;  /* False positive: separator in middle of line */
     297                 :            :     
     298   [ #  #  #  #  :          0 :     if (start && parts && nparts < max_parts) {      
                   #  # ]
     299                 :          0 :       len = lengths[nparts] = pp-start;
     300         [ #  # ]:          0 :       if (!(parts[nparts] = (char*)OPENSSL_malloc(len+1))) GOTO_ERR("no memory?");
     301                 :          0 :       memcpy(parts[nparts], start, len);
     302                 :          0 :       parts[nparts][len] = '\0';  /* Gratuitous nul termination */
     303                 :            :     }
     304                 :            :     
     305                 :          0 :     nparts++;
     306         [ #  # ]:          0 :     if (*p == '\015') p++;  /* Skip CRLF */
     307         [ #  # ]:          0 :     if (*p == '\012') p++;  /* This is really mandatory */
     308                 :            :     
     309                 :          0 :     start = p;
     310                 :            :   }
     311                 :          0 :   return nparts;
     312                 :            : 
     313                 :            : /* Called by: */
     314                 :          0 : err:
     315         [ #  # ]:          0 :   if (parts) {
     316                 :            :     /* free what we have allocated so far */
     317         [ #  # ]:          0 :     for (sep_len = 0; sep_len < nparts; sep_len++)
     318         [ #  # ]:          0 :       if (parts[sep_len])
     319                 :          0 :         OPENSSL_free(parts[sep_len]);
     320                 :            :   }
     321                 :          0 :   return -1;
     322                 :            : }
     323                 :            : 
     324                 :            : /* Called by:  main */
     325                 :            : char*
     326                 :            : mime_raw_entity(const char* text, const char* type)
     327                 :          7 : {
     328                 :            :   char* b;  
     329         [ -  + ]:          7 :   if (!(b = strdup("Content-type: "))) GOTO_ERR("no memory?");
     330         [ +  - ]:          7 :   if (!(b = concat(b, type))) goto err;
     331         [ +  - ]:          7 :   if (!(b = concat(b, CRLF CRLF))) goto err;
     332         [ +  - ]:          7 :   if (!(b = concat(b, text))) goto err;
     333                 :          7 :   return b;
     334                 :          0 : err:
     335                 :          0 :   return NULL;
     336                 :            : }
     337                 :            : 
     338                 :            : /* Called by:  main x3 */
     339                 :            : char*
     340                 :            : mime_base64_entity(const char* data, int len, const char* type)
     341                 :          3 : {
     342                 :            :   int n;
     343                 :            :   char* b64;
     344                 :            :   char* b;  
     345         [ -  + ]:          3 :   if (!(b = strdup("Content-type: "))) GOTO_ERR("no memory?");
     346         [ +  - ]:          3 :   if (!(b = concat(b, type))) goto err;
     347         [ +  - ]:          3 :   if (!(b = concat(b, CRLF CRLF))) goto err;
     348                 :            :   
     349                 :          3 :   n = smime_base64(1, data, len, &b64);
     350         [ -  + ]:          3 :   if (!b64) GOTO_ERR("no memory?");
     351         [ +  - ]:          3 :   if (!(b = concat(b, b64))) goto err;
     352                 :          3 :   return b;
     353                 :          0 : err:
     354                 :          0 :   return NULL;
     355                 :            : }
     356                 :            : 
     357                 :            : /* Canonicalization involves converting LF->CRLF (Unix) and CR->CRLF (Mac).
     358                 :            :  * Canonicalization is of prime importance when signing data because
     359                 :            :  * verification assumes canonicalized form. This canonicalization is really
     360                 :            :  * not mime specific at all so you can use it for fixing PEM blobs
     361                 :            :  * on Mac (becaue OpenSSL does not understand lone CR as line termination). */
     362                 :            : 
     363                 :            : /* Called by:  clear_sign, extract_certificate, extract_request, main, open_private_key, sign */
     364                 :            : char*
     365                 :            : mime_canon(const char* s)
     366                 :          1 : {
     367                 :            :   char* d;
     368                 :            :   char* p;
     369                 :            :   int len;
     370                 :          1 :   len = strlen(s);
     371                 :          1 :   p = d = (char*)OPENSSL_malloc(len + len);  /* Reserve spaces for CR's to be inserted. */
     372         [ -  + ]:          1 :   if (!d) GOTO_ERR("no memory?");
     373                 :            :   
     374                 :            :   /* Scan and copy */
     375                 :            : 
     376         [ +  + ]:        571 :   for (; *s; s++) {
     377   [ +  +  +  + ]:       1130 :     if (s[0] != '\015' && s[0] != '\012')
     378                 :        560 :       *(p++) = *s; /* pass thru */
     379                 :            :     else {
     380   [ +  +  +  - ]:         10 :       if (s[0] == '\015' && s[1] == '\012') s++;  /* already CRLF */
     381                 :         10 :       *(p++) = '\015'; *(p++) = '\012';
     382                 :            :     }
     383                 :            :   }
     384                 :          1 :   *(p++) = '\0';
     385                 :            : 
     386                 :            :   /* Shrink the buffer back to actual size (not very likely to fail) */
     387                 :            : 
     388                 :          1 :   return (char*)OPENSSL_realloc(d, (int)p-(int)d);
     389                 :          0 : err:
     390                 :          0 :   return NULL;
     391                 :            : }
     392                 :            : 
     393                 :            : /* EOF  -  smimemime.c */

Generated by: LCOV version 1.9