-#ifndef DISABLE_DKIM
-
-/***************************************************************************************************
-* External interface to write the message, while signing it with DKIM and/or Domainkeys *
-***************************************************************************************************/
-
-/* This function is a wrapper around transport_write_message(). It is only called
- from the smtp transport if DKIM or Domainkeys support is compiled in.
- The function sets up a replacement fd into a -K file, then calls the normal
- function. This way, the exact bits that exim would have put "on the wire" will
- end up in the file (except for TLS encapsulation, which is the very
- very last thing). When we are done signing the file, send the
- signed message down the original fd (or TLS fd).
-
-Arguments: as for internal_transport_write_message() above, with additional
- arguments:
- uschar *dkim_private_key DKIM: The private key to use (filename or plain data)
- uschar *dkim_domain DKIM: The domain to use
- uschar *dkim_selector DKIM: The selector to use.
- uschar *dkim_canon DKIM: The canonalization scheme to use, "simple" or "relaxed"
- uschar *dkim_strict DKIM: What to do if signing fails: 1/true => throw error
- 0/false => send anyway
- uschar *dkim_sign_headers DKIM: List of headers that should be included in signature
- generation
-
-Returns: TRUE on success; FALSE (with errno) for any failure
-*/
-
-BOOL
-dkim_transport_write_message(address_item *addr, int fd, int options,
- int size_limit, uschar *add_headers, uschar *remove_headers,
- uschar *check_string, uschar *escape_string, rewrite_rule *rewrite_rules,
- int rewrite_existflags, uschar *dkim_private_key, uschar *dkim_domain,
- uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers
- )
-{
- int dkim_fd;
- int save_errno = 0;
- BOOL rc;
- uschar dkim_spool_name[256];
- char sbuf[2048];
- int sread = 0;
- int wwritten = 0;
- uschar *dkim_signature = NULL;
- off_t size = 0;
-
- if (!( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) )) {
- /* If we can't sign, just call the original function. */
- return transport_write_message(addr, fd, options,
- size_limit, add_headers, remove_headers,
- check_string, escape_string, rewrite_rules,
- rewrite_existflags);
- }
-
- (void)string_format(dkim_spool_name, 256, "%s/input/%s/%s-%d-K",
- spool_directory, message_subdir, message_id, (int)getpid());
- dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE);
- if (dkim_fd < 0)
- {
- /* Can't create spool file. Ugh. */
- rc = FALSE;
- save_errno = errno;
- goto CLEANUP;
- }
-
- /* Call original function */
- rc = transport_write_message(addr, dkim_fd, options,
- size_limit, add_headers, remove_headers,
- check_string, escape_string, rewrite_rules,
- rewrite_existflags);
-
- /* Save error state. We must clean up before returning. */
- if (!rc)
- {
- save_errno = errno;
- goto CLEANUP;
- }
-
- if ( (dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL) ) {
- /* Rewind file and feed it to the goats^W DKIM lib */
- lseek(dkim_fd, 0, SEEK_SET);
- dkim_signature = dkim_exim_sign(dkim_fd,
- dkim_private_key,
- dkim_domain,
- dkim_selector,
- dkim_canon,
- dkim_sign_headers);
- if (dkim_signature == NULL) {
- if (dkim_strict != NULL) {
- uschar *dkim_strict_result = expand_string(dkim_strict);
- if (dkim_strict_result != NULL) {
- if ( (strcmpic(dkim_strict,US"1") == 0) ||
- (strcmpic(dkim_strict,US"true") == 0) ) {
- /* Set errno to something halfway meaningful */
- save_errno = EACCES;
- log_write(0, LOG_MAIN, "DKIM: message could not be signed, and dkim_strict is set. Deferring message delivery.");
- rc = FALSE;
- goto CLEANUP;
- }
- }
- }
- }
- else {
- int siglen = Ustrlen(dkim_signature);
- while(siglen > 0) {
- #ifdef SUPPORT_TLS
- if (tls_out.active == fd) wwritten = tls_write(FALSE, dkim_signature, siglen); else
- #endif
- wwritten = write(fd,dkim_signature,siglen);
- if (wwritten == -1) {
- /* error, bail out */
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
- siglen -= wwritten;
- dkim_signature += wwritten;
- }
- }
- }
-
- /* Fetch file positition (the size) */
- size = lseek(dkim_fd,0,SEEK_CUR);
-
- /* Rewind file */
- lseek(dkim_fd, 0, SEEK_SET);
-
-#ifdef HAVE_LINUX_SENDFILE
- /* We can use sendfile() to shove the file contents
- to the socket. However only if we don't use TLS,
- in which case theres another layer of indirection
- before the data finally hits the socket. */
- if (tls_out.active != fd)
- {
- ssize_t copied = 0;
- off_t offset = 0;
- while((copied >= 0) && (offset<size))
- {
- copied = sendfile(fd, dkim_fd, &offset, (size - offset));
- }
- if (copied < 0)
- {
- save_errno = errno;
- rc = FALSE;
- }
- goto CLEANUP;
- }
-#endif
-
- /* Send file down the original fd */
- while((sread = read(dkim_fd,sbuf,2048)) > 0)
- {
- char *p = sbuf;
- /* write the chunk */
- DKIM_WRITE:
- #ifdef SUPPORT_TLS
- if (tls_out.active == fd) wwritten = tls_write(FALSE, US p, sread); else
- #endif
- wwritten = write(fd,p,sread);
- if (wwritten == -1)
- {
- /* error, bail out */
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
- if (wwritten < sread)
- {
- /* short write, try again */
- p += wwritten;
- sread -= wwritten;
- goto DKIM_WRITE;
- }
- }
-
- if (sread == -1)
- {
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
-
- CLEANUP:
- /* unlink -K file */
- (void)close(dkim_fd);
- Uunlink(dkim_spool_name);
- errno = save_errno;
- return rc;
-}
-
-#endif
-