Example #1
0
File: mime.c Project: akissa/exim
int
mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *context,
    uschar **user_msgptr, uschar **log_msgptr)
{
int rc = OK;
uschar * header = NULL;
struct mime_boundary_context nested_context;

/* reserve a line buffer to work in */
header = store_get(MIME_MAX_HEADER_SIZE+1);

/* Not actually used at the moment, but will be vital to fixing
 * some RFC 2046 nonconformance later... */
nested_context.parent = context;

/* loop through parts */
while(1)
  {
  /* reset all per-part mime variables */
  mime_vars_reset();

  /* If boundary is null, we assume that *f is positioned on the start of
  headers (for example, at the very beginning of a message.  If a boundary is
  given, we must first advance to it to reach the start of the next header
  block.  */

  /* NOTE -- there's an error here -- RFC2046 specifically says to
   * check for outer boundaries.  This code doesn't do that, and
   * I haven't fixed this.
   *
   * (I have moved partway towards adding support, however, by adding
   * a "parent" field to my new boundary-context structure.)
   */
  if (context) for (;;)
    {
    if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f))
      {
      /* Hit EOF or read error. Ugh. */
      DEBUG(D_acl) debug_printf("MIME: Hit EOF ...\n");
      return rc;
      }

    /* boundary line must start with 2 dashes */
    if (  Ustrncmp(header, "--", 2) == 0
       && Ustrncmp(header+2, context->boundary, Ustrlen(context->boundary)) == 0
       )
      {			/* found boundary */
      if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0)
	{
	/* END boundary found */
	DEBUG(D_acl) debug_printf("MIME: End boundary found %s\n",
	  context->boundary);
	return rc;
	}

      DEBUG(D_acl) debug_printf("MIME: Next part with boundary %s\n",
	context->boundary);
      break;
      }
    }

  /* parse headers, set up expansion variables */
  while (mime_get_header(f, header))
    {
    struct mime_header * mh;

    /* look for interesting headers */
    for (mh = mime_header_list;
	 mh < mime_header_list + mime_header_list_size;
	 mh++) if (strncmpic(mh->name, header, mh->namelen) == 0)
      {
      uschar * p = header + mh->namelen;
      uschar * q;

      /* grab the value (normalize to lower case)
      and copy to its corresponding expansion variable */

      for (q = p; *q != ';' && *q; q++) ;
      *mh->value = string_copynlc(p, q-p);
      DEBUG(D_acl) debug_printf("MIME: found %s header, value is '%s'\n",
	mh->name, *mh->value);

      if (*(p = q)) p++;			/* jump past the ; */

	{
	uschar * mime_fname = NULL;
	uschar * mime_fname_rfc2231 = NULL;
	uschar * mime_filename_charset = NULL;
	BOOL decoding_failed = FALSE;

	/* grab all param=value tags on the remaining line,
	check if they are interesting */

	while (*p)
	  {
	  mime_parameter * mp;

	  DEBUG(D_acl) debug_printf("MIME:   considering paramlist '%s'\n", p);

	  if (  !mime_filename
	     && strncmpic(CUS"content-disposition:", header, 20) == 0
	     && strncmpic(CUS"filename*", p, 9) == 0
	     )
	    {					/* RFC 2231 filename */
	    uschar * q;

	    /* find value of the filename */
	    p += 9;
	    while(*p != '=' && *p) p++;
	    if (*p) p++;			/* p is filename or NUL */
	    q = mime_param_val(&p);		/* p now trailing ; or NUL */

	    if (q && *q)
	      {
	      uschar * temp_string, * err_msg;
	      int slen;

	      /* build up an un-decoded filename over successive
	      filename*= parameters (for use when 2047 decode fails) */

	      mime_fname_rfc2231 = string_sprintf("%#s%s",
		mime_fname_rfc2231, q);

	      if (!decoding_failed)
		{
		int size;
		if (!mime_filename_charset)
		  {
		  uschar * s = q;

		  /* look for a ' in the "filename" */
		  while(*s != '\'' && *s) s++;	/* s is 1st ' or NUL */

		  if ((size = s-q) > 0)
		    mime_filename_charset = string_copyn(q, size);

		  if (*(p = s)) p++;
		  while(*p == '\'') p++;	/* p is after 2nd ' */
		  }
		else
		  p = q;

		DEBUG(D_acl) debug_printf("MIME:    charset %s fname '%s'\n",
		  mime_filename_charset ? mime_filename_charset : US"<NULL>", p);

		temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen);
		DEBUG(D_acl) debug_printf("MIME:    2047-name %s\n", temp_string);

		temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ',
		  NULL, &err_msg);
		DEBUG(D_acl) debug_printf("MIME:    plain-name %s\n", temp_string);

		size = Ustrlen(temp_string);

		if (size == slen)
		  decoding_failed = TRUE;
		else
		  /* build up a decoded filename over successive
		  filename*= parameters */

		  mime_filename = mime_fname = mime_fname
		    ? string_sprintf("%s%s", mime_fname, temp_string)
		    : temp_string;
		}
	      }
	    }

	  else
	    /* look for interesting parameters */
	    for (mp = mime_parameter_list;
		 mp < mime_parameter_list + nelem(mime_parameter_list);
		 mp++
		) if (strncmpic(mp->name, p, mp->namelen) == 0)
	      {
	      uschar * q;
	      uschar * dummy_errstr;

	      /* grab the value and copy to its expansion variable */
	      p += mp->namelen;
	      q = mime_param_val(&p);		/* p now trailing ; or NUL */

	      *mp->value = q && *q
		? rfc2047_decode(q, check_rfc2047_length, NULL, 32, NULL,
		    &dummy_errstr)
		: NULL;
	      DEBUG(D_acl) debug_printf(
		"MIME:  found %s parameter in %s header, value '%s'\n",
		mp->name, mh->name, *mp->value);

	      break;			/* done matching param names */
	      }


	  /* There is something, but not one of our interesting parameters.
	     Advance past the next semicolon */
	  p = mime_next_semicolon(p);
	  if (*p) p++;
	  }				/* param scan on line */

	if (strncmpic(CUS"content-disposition:", header, 20) == 0)
	  {
	  if (decoding_failed) mime_filename = mime_fname_rfc2231;

	  DEBUG(D_acl) debug_printf(
	    "MIME:  found %s parameter in %s header, value is '%s'\n",
	    "filename", mh->name, mime_filename);
	  }
	}
      }
    }

  /* set additional flag variables (easier access) */
  if (  mime_content_type
     && Ustrncmp(mime_content_type,"multipart",9) == 0
     )
    mime_is_multipart = 1;

  /* Make a copy of the boundary pointer.
     Required since mime_boundary is global
     and can be overwritten further down in recursion */
  nested_context.boundary = mime_boundary;

  /* raise global counter */
  mime_part_count++;

  /* copy current file handle to global variable */
  mime_stream = f;
  mime_current_boundary = context ? context->boundary : 0;

  /* Note the context */
  mime_is_coverletter = !(context && context->context == MBC_ATTACHMENT);

  /* call ACL handling function */
  rc = acl_check(ACL_WHERE_MIME, NULL, acl, user_msgptr, log_msgptr);

  mime_stream = NULL;
  mime_current_boundary = NULL;

  if (rc != OK) break;

  /* If we have a multipart entity and a boundary, go recursive */
  if ( (mime_content_type != NULL) &&
       (nested_context.boundary != NULL) &&
       (Ustrncmp(mime_content_type,"multipart",9) == 0) )
    {
    DEBUG(D_acl)
      debug_printf("MIME: Entering multipart recursion, boundary '%s'\n",
	nested_context.boundary);

    nested_context.context =
      context && context->context == MBC_ATTACHMENT
      ? MBC_ATTACHMENT
      :    Ustrcmp(mime_content_type,"multipart/alternative") == 0
	|| Ustrcmp(mime_content_type,"multipart/related") == 0
      ? MBC_COVERLETTER_ALL
      : MBC_COVERLETTER_ONESHOT;

    rc = mime_acl_check(acl, f, &nested_context, user_msgptr, log_msgptr);
    if (rc != OK) break;
    }
  else if ( (mime_content_type != NULL) &&
	  (Ustrncmp(mime_content_type,"message/rfc822",14) == 0) )
    {
    const uschar *rfc822name = NULL;
    uschar filename[2048];
    int file_nr = 0;
    int result = 0;

    /* must find first free sequential filename */
    do
      {
      struct stat mystat;
      (void)string_format(filename, 2048,
	"%s/scan/%s/__rfc822_%05u", spool_directory, message_id, file_nr++);
      /* security break */
      if (file_nr >= 128)
	goto NO_RFC822;
      result = stat(CS filename,&mystat);
      } while (result != -1);

    rfc822name = filename;

    /* decode RFC822 attachment */
    mime_decoded_filename = NULL;
    mime_stream = f;
    mime_current_boundary = context ? context->boundary : NULL;
    mime_decode(&rfc822name);
    mime_stream = NULL;
    mime_current_boundary = NULL;
    if (!mime_decoded_filename)		/* decoding failed */
      {
      log_write(0, LOG_MAIN,
	   "mime_regex acl condition warning - could not decode RFC822 MIME part to file.");
      rc = DEFER;
      goto out;
      }
    mime_decoded_filename = NULL;
    }

NO_RFC822:
  /* If the boundary of this instance is NULL, we are finished here */
  if (!context) break;

  if (context->context == MBC_COVERLETTER_ONESHOT)
    context->context = MBC_ATTACHMENT;
  }

out:
mime_vars_reset();
return rc;
}
Example #2
0
File: regex.c Project: fanf2/exim
int mime_regex(uschar **listptr) {
  int sep = 0;
  uschar *list = *listptr;
  uschar *regex_string;
  uschar regex_string_buffer[1024];
  pcre *re;
  pcre_list *re_list_head = NULL;
  pcre_list *re_list_item;
  const char *pcre_error;
  int pcre_erroffset;
  FILE *f;
  uschar *mime_subject = NULL;
  int mime_subject_len = 0;

  /* reset expansion variable */
  regex_match_string = NULL;

  /* precompile our regexes */
  while ((regex_string = string_nextinlist(&list, &sep,
                                           regex_string_buffer,
                                           sizeof(regex_string_buffer))) != NULL) {

    /* parse option */
    if ( (strcmpic(regex_string,US"false") == 0) ||
         (Ustrcmp(regex_string,"0") == 0) ) {
      /* explicitly no matching */
      continue;
    };

    /* compile our regular expression */
    re = pcre_compile( CS regex_string,
                       0,
                       &pcre_error,
                       &pcre_erroffset,
                       NULL );

    if (re == NULL) {
      log_write(0, LOG_MAIN,
           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
      continue;
    }
    else {
      re_list_item = store_get(sizeof(pcre_list));
      re_list_item->re = re;
      re_list_item->pcre_text = string_copy(regex_string);
      re_list_item->next = re_list_head;
      re_list_head = re_list_item;
    };
  };

  /* no regexes -> nothing to do */
  if (re_list_head == NULL) {
    return FAIL;
  };

  /* check if the file is already decoded */
  if (mime_decoded_filename == NULL) {
    uschar *empty = US"";
    /* no, decode it first */
    mime_decode(&empty);
    if (mime_decoded_filename == NULL) {
      /* decoding failed */
      log_write(0, LOG_MAIN,
           "mime_regex acl condition warning - could not decode MIME part to file.");
      return DEFER;
    };
  };


  /* open file */
  f = fopen(CS mime_decoded_filename, "rb");
  if (f == NULL) {
    /* open failed */
    log_write(0, LOG_MAIN,
         "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
    return DEFER;
  };

  /* get 32k memory */
  mime_subject = (uschar *)store_get(32767);

  /* read max 32k chars from file */
  mime_subject_len = fread(mime_subject, 1, 32766, f);

  re_list_item = re_list_head;
  do {
    /* try matcher on the mmapped file */
    debug_printf("Matching '%s'\n", re_list_item->pcre_text);
    if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
                  mime_subject_len, 0, 0, NULL, 0) >= 0) {
      Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
      regex_match_string = regex_match_string_buffer;
      (void)fclose(f);
      return OK;
    };
    re_list_item = re_list_item->next;
  } while (re_list_item != NULL);

  (void)fclose(f);

  /* no matches ... */
  return FAIL;
}
Example #3
0
/*
 * Break out the components of a multipart message
 * (This function expects to be fed HEADERS + CONTENT)
 * Note: NULL can be supplied as content_end; in this case, the message is
 * considered to have ended when the parser encounters a 0x00 byte.
 */
static void recurseable_mime_parser(char *partnum,
				    char *content_start, char *content_end,
				    MimeParserCallBackType CallBack,
				    MimeParserCallBackType PreMultiPartCallBack,
				    MimeParserCallBackType PostMultiPartCallBack,
				    void *userdata,
				    int dont_decode, 
				    interesting_mime_headers *m)
{
	interesting_mime_headers *SubMimeHeaders;
	char     *ptr;
	char     *part_start;
	char     *part_end = NULL;
	char     *evaluate_crlf_ptr = NULL;
	char     *next_boundary;
	char      nested_partnum[256];
	int       crlf_in_use = 0;
	int       part_seq = 0;
	CBufStr  *chosen_name;


	/* If this is a multipart message, then recursively process it */
	ptr = content_start;
	part_start = NULL;
	if (m->is_multipart) {

		/* Tell the client about this message's multipartedness */
		if (PreMultiPartCallBack != NULL) {
			PreMultiPartCallBack("", 
					     "", 
					     partnum, 
					     "",
					     NULL, 
					     m->b[content_type].Key, 
					     m->b[charset].Key,
					     0, 
					     m->b[encoding].Key, 
					     m->b[id].Key, 
					     userdata);
		}

		/* Figure out where the boundaries are */
		m->b[startary].len = snprintf(m->b[startary].Key, SIZ, "--%s", m->b[boundary].Key);
		SubMimeHeaders = InitInterestingMimes ();

		while ((*ptr == '\r') || (*ptr == '\n')) ptr ++;

		if (strncmp(ptr, m->b[startary].Key, m->b[startary].len) == 0)
			ptr += m->b[startary].len;

		while ((*ptr == '\r') || (*ptr == '\n')) ptr ++;

		part_start = NULL;
		do {
			char *optr;

			optr = ptr;
			if (parse_MimeHeaders(SubMimeHeaders, &ptr, content_end) != 0)
				break;
			if ((ptr - optr > 2) && 
			    (*(ptr - 2) == '\r'))
				crlf_in_use = 1;
			
			part_start = ptr;
			
			next_boundary = FindNextContent(ptr,
							content_end,
							SubMimeHeaders,
							m);
			if ((next_boundary != NULL) && 
			    (next_boundary - part_start < 3)) {
				FlushInterestingMimes(SubMimeHeaders);

				continue;
			}

			if ( (part_start != NULL) && (next_boundary != NULL) ) {
				part_end = next_boundary;
				--part_end;		/* omit the trailing LF */
				if (crlf_in_use) {
					--part_end;	/* omit the trailing CR */
				}

				if (!IsEmptyStr(partnum)) {
					snprintf(nested_partnum,
						 sizeof nested_partnum,
						 "%s.%d", partnum,
						 ++part_seq);
				}
				else {
					snprintf(nested_partnum,
						 sizeof nested_partnum,
						 "%d", ++part_seq);
				}
				recurseable_mime_parser(nested_partnum,
							part_start, 
							part_end,
							CallBack,
							PreMultiPartCallBack,
							PostMultiPartCallBack,
							userdata,
							dont_decode, 
							SubMimeHeaders);
			}

			if (next_boundary != NULL) {
				/* If we pass out of scope, don't attempt to
				 * read past the end boundary. */
				if ((*(next_boundary + m->b[startary].len) == '-') && 
				    (*(next_boundary + m->b[startary].len + 1) == '-') ){
					ptr = content_end;
				}
				else {
					/* Set up for the next part. */
					part_start = strstr(next_boundary, "\n");
					
					/* Determine whether newlines are LF or CRLF */
					evaluate_crlf_ptr = part_start;
					--evaluate_crlf_ptr;
					if ((*evaluate_crlf_ptr == '\r') && 
					    (*(evaluate_crlf_ptr + 1) == '\n'))
					{
						crlf_in_use = 1;
					}
					else {
						crlf_in_use = 0;
					}

					/* Advance past the LF ... now we're in the next part */
					++part_start;
					ptr = part_start;
				}
			}
			else {
				/* Invalid end of multipart.  Bail out! */
				ptr = content_end;
			}
			FlushInterestingMimes(SubMimeHeaders);
		} while ( (ptr < content_end) && (next_boundary != NULL) );

		free(SubMimeHeaders);

		if (PostMultiPartCallBack != NULL) {
			PostMultiPartCallBack("", 
					      "", 
					      partnum, 
					      "", 
					      NULL,
					      m->b[content_type].Key, 
					      m->b[charset].Key,
					      0, 
					      m->b[encoding].Key, 
					      m->b[id].Key, 
					      userdata);
		}
	} /* If it's not a multipart message, then do something with it */
	else {
		size_t length;
		part_start = ptr;
		length = content_end - part_start;
		ptr = part_end = content_end;


		/* The following code will truncate the MIME part to the size
		 * specified by the Content-length: header.   We have commented it
		 * out because these headers have a tendency to be wrong.
		 *
		 *	if ( (content_length > 0) && (length > content_length) ) {
		 *		length = content_length;
		 *	}
                 */

		/* Sometimes the "name" field is tacked on to Content-type,
		 * and sometimes it's tacked on to Content-disposition.  Use
		 * whichever one we have.
		 */
		if (m->b[content_disposition_name].len > m->b[content_type_name].len) {
			chosen_name = &m->b[content_disposition_name];
		}
		else {
			chosen_name = &m->b[content_type_name];
		}
	
		/* Ok, we've got a non-multipart part here, so do something with it.
		 */
		mime_decode(partnum,
			    part_start, 
			    length,
			    m->b[content_type].Key, 
			    m->b[charset].Key,
			    m->b[encoding].Key, 
			    m->b[disposition].Key, 
			    m->b[id].Key, 
			    chosen_name->Key, 
			    m->b[filename].Key,
			    CallBack, 
			    NULL, NULL,
			    userdata, 
			    dont_decode
			);

		/*
		 * Now if it's an encapsulated message/rfc822 then we have to recurse into it
		 */
		if (!strcasecmp(&m->b[content_type].Key[0], "message/rfc822")) {

			if (PreMultiPartCallBack != NULL) {
				PreMultiPartCallBack("", 
						     "", 
						     partnum, 
						     "",
						     NULL, 
						     m->b[content_type].Key, 
						     m->b[charset].Key,
						     0, 
						     m->b[encoding].Key, 
						     m->b[id].Key, 
						     userdata);
			}
			if (CallBack != NULL) {
				if (strlen(partnum) > 0) {
					snprintf(nested_partnum,
						 sizeof nested_partnum,
						 "%s.%d", partnum,
						 ++part_seq);
				}
				else {
					snprintf(nested_partnum,
						 sizeof nested_partnum,
						 "%d", ++part_seq);
				}
				the_mime_parser(nested_partnum,
						part_start, 
						part_end,
						CallBack,
						PreMultiPartCallBack,
						PostMultiPartCallBack,
						userdata,
						dont_decode
					);
			}
			if (PostMultiPartCallBack != NULL) {
				PostMultiPartCallBack("", 
						      "", 
						      partnum, 
						      "", 
						      NULL,
						      m->b[content_type].Key, 
						      m->b[charset].Key,
						      0, 
						      m->b[encoding].Key, 
						      m->b[id].Key, 
						      userdata);
			}


		}

	}

}