~ubuntu-branches/debian/jessie/exim4/jessie

« back to all changes in this revision

Viewing changes to src/dmarc.c

  • Committer: Package Import Robot
  • Author(s): Andreas Metzler
  • Date: 2014-08-02 09:29:13 UTC
  • mfrom: (1.6.5)
  • Revision ID: package-import@ubuntu.com-20140802092913-lksj1ulfd2cqfoqe
Tags: 4.84~RC1-3
Third try. Simply comment *custom* in debian/control.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
 
13
13
#include "exim.h"
14
14
#ifdef EXPERIMENTAL_DMARC
15
 
#if !defined EXPERIMENTAL_SPF
16
 
#error SPF must also be enabled for DMARC
17
 
#elif defined DISABLE_DKIM
18
 
#error DKIM must also be enabled for DMARC
19
 
#else
 
15
# if !defined EXPERIMENTAL_SPF
 
16
#  error SPF must also be enabled for DMARC
 
17
# elif defined DISABLE_DKIM
 
18
#  error DKIM must also be enabled for DMARC
 
19
# else
20
20
 
21
 
#include "functions.h"
22
 
#include "dmarc.h"
23
 
#include "pdkim/pdkim.h"
 
21
#  include "functions.h"
 
22
#  include "dmarc.h"
 
23
#  include "pdkim/pdkim.h"
24
24
 
25
25
OPENDMARC_LIB_T     dmarc_ctx;
26
26
DMARC_POLICY_T     *dmarc_pctx = NULL;
57
57
static error_block *
58
58
add_to_eblock(error_block *eblock, uschar *t1, uschar *t2)
59
59
{
60
 
  error_block *eb = malloc(sizeof(error_block));
61
 
  if (eblock == NULL)
62
 
    eblock = eb;
63
 
  else
 
60
error_block *eb = malloc(sizeof(error_block));
 
61
if (eblock == NULL)
 
62
  eblock = eb;
 
63
else
64
64
  {
65
 
    /* Find the end of the eblock struct and point it at eb */
66
 
    error_block *tmp = eblock;
67
 
    while(tmp->next != NULL)
68
 
      tmp = tmp->next;
69
 
    tmp->next = eb;
 
65
  /* Find the end of the eblock struct and point it at eb */
 
66
  error_block *tmp = eblock;
 
67
  while(tmp->next != NULL)
 
68
    tmp = tmp->next;
 
69
  tmp->next = eb;
70
70
  }
71
 
  eb->text1 = t1;
72
 
  eb->text2 = t2;
73
 
  eb->next  = NULL;
74
 
  return eblock;
 
71
eb->text1 = t1;
 
72
eb->text2 = t2;
 
73
eb->next  = NULL;
 
74
return eblock;
75
75
}
76
76
 
77
77
/* dmarc_init sets up a context that can be re-used for several
80
80
 
81
81
int dmarc_init()
82
82
{
83
 
  int *netmask   = NULL;   /* Ignored */
84
 
  int is_ipv6    = 0;
85
 
  char *tld_file = (dmarc_tld_file == NULL) ?
86
 
                   "/etc/exim/opendmarc.tlds" :
87
 
                   (char *)dmarc_tld_file;
88
 
 
89
 
  /* Set some sane defaults.  Also clears previous results when
90
 
   * multiple messages in one connection. */
91
 
  dmarc_pctx         = NULL;
92
 
  dmarc_status       = US"none";
93
 
  dmarc_abort        = FALSE;
94
 
  dmarc_pass_fail    = US"skipped";
95
 
  dmarc_used_domain  = US"";
96
 
  dmarc_ar_header    = NULL;
97
 
  dmarc_has_been_checked = FALSE;
98
 
  header_from_sender = NULL;
99
 
  spf_sender_domain  = NULL;
100
 
  spf_human_readable = NULL;
101
 
 
102
 
  /* ACLs have "control=dmarc_disable_verify" */
103
 
  if (dmarc_disable_verify == TRUE)
104
 
    return OK;
105
 
 
106
 
  (void) memset(&dmarc_ctx, '\0', sizeof dmarc_ctx);
107
 
  dmarc_ctx.nscount = 0;
108
 
  libdm_status = opendmarc_policy_library_init(&dmarc_ctx);
109
 
  if (libdm_status != DMARC_PARSE_OKAY)
110
 
  {
111
 
    log_write(0, LOG_MAIN|LOG_PANIC, "DMARC failure to init library: %s",
112
 
                         opendmarc_policy_status_to_str(libdm_status));
113
 
    dmarc_abort = TRUE;
114
 
  }
115
 
  if (dmarc_tld_file == NULL)
116
 
    dmarc_abort = TRUE;
117
 
  else if (opendmarc_tld_read_file(tld_file, NULL, NULL, NULL))
118
 
  {
119
 
    log_write(0, LOG_MAIN|LOG_PANIC, "DMARC failure to load tld list %s: %d",
120
 
                         tld_file, errno);
121
 
    dmarc_abort = TRUE;
122
 
  }
123
 
  if (sender_host_address == NULL)
124
 
    dmarc_abort = TRUE;
125
 
  /* This catches locally originated email and startup errors above. */
126
 
  if ( dmarc_abort == FALSE )
127
 
  {
128
 
    is_ipv6 = string_is_ip_address(sender_host_address, netmask);
129
 
    is_ipv6 = (is_ipv6 == 6) ? TRUE :
130
 
              (is_ipv6 == 4) ? FALSE : FALSE;
131
 
    dmarc_pctx = opendmarc_policy_connect_init(sender_host_address, is_ipv6);
132
 
    if (dmarc_pctx == NULL )
 
83
int *netmask   = NULL;   /* Ignored */
 
84
int is_ipv6    = 0;
 
85
char *tld_file = (dmarc_tld_file == NULL) ?
 
86
                 "/etc/exim/opendmarc.tlds" :
 
87
                 (char *)dmarc_tld_file;
 
88
 
 
89
/* Set some sane defaults.  Also clears previous results when
 
90
 * multiple messages in one connection. */
 
91
dmarc_pctx         = NULL;
 
92
dmarc_status       = US"none";
 
93
dmarc_abort        = FALSE;
 
94
dmarc_pass_fail    = US"skipped";
 
95
dmarc_used_domain  = US"";
 
96
dmarc_ar_header    = NULL;
 
97
dmarc_has_been_checked = FALSE;
 
98
header_from_sender = NULL;
 
99
spf_sender_domain  = NULL;
 
100
spf_human_readable = NULL;
 
101
 
 
102
/* ACLs have "control=dmarc_disable_verify" */
 
103
if (dmarc_disable_verify == TRUE)
 
104
  return OK;
 
105
 
 
106
(void) memset(&dmarc_ctx, '\0', sizeof dmarc_ctx);
 
107
dmarc_ctx.nscount = 0;
 
108
libdm_status = opendmarc_policy_library_init(&dmarc_ctx);
 
109
if (libdm_status != DMARC_PARSE_OKAY)
 
110
  {
 
111
  log_write(0, LOG_MAIN|LOG_PANIC, "DMARC failure to init library: %s",
 
112
                       opendmarc_policy_status_to_str(libdm_status));
 
113
  dmarc_abort = TRUE;
 
114
  }
 
115
if (dmarc_tld_file == NULL)
 
116
  dmarc_abort = TRUE;
 
117
else if (opendmarc_tld_read_file(tld_file, NULL, NULL, NULL))
 
118
  {
 
119
  log_write(0, LOG_MAIN|LOG_PANIC, "DMARC failure to load tld list %s: %d",
 
120
                       tld_file, errno);
 
121
  dmarc_abort = TRUE;
 
122
  }
 
123
if (sender_host_address == NULL)
 
124
  dmarc_abort = TRUE;
 
125
/* This catches locally originated email and startup errors above. */
 
126
if (!dmarc_abort)
 
127
  {
 
128
  is_ipv6 = string_is_ip_address(sender_host_address, netmask) == 6;
 
129
  dmarc_pctx = opendmarc_policy_connect_init(sender_host_address, is_ipv6);
 
130
  if (dmarc_pctx == NULL)
133
131
    {
134
 
      log_write(0, LOG_MAIN|LOG_PANIC, "DMARC failure creating policy context: ip=%s",
135
 
                                       sender_host_address);
136
 
      dmarc_abort = TRUE;
 
132
    log_write(0, LOG_MAIN|LOG_PANIC,
 
133
      "DMARC failure creating policy context: ip=%s", sender_host_address);
 
134
    dmarc_abort = TRUE;
137
135
    }
138
136
  }
139
137
 
140
 
  return OK;
 
138
return OK;
141
139
}
142
140
 
143
141
 
156
154
   context (if any), retrieves the result, sets up expansion
157
155
   strings and evaluates the condition outcome. */
158
156
 
159
 
int dmarc_process() {
160
 
    int sr, origin;             /* used in SPF section */
161
 
    int dmarc_spf_result  = 0;  /* stores spf into dmarc conn ctx */
162
 
    int tmp_ans, c;
163
 
    pdkim_signature *sig  = NULL;
164
 
    BOOL has_dmarc_record = TRUE;
165
 
    u_char **ruf; /* forensic report addressees, if called for */
 
157
int
 
158
dmarc_process()
 
159
{
 
160
int sr, origin;             /* used in SPF section */
 
161
int dmarc_spf_result  = 0;  /* stores spf into dmarc conn ctx */
 
162
int tmp_ans, c;
 
163
pdkim_signature *sig  = NULL;
 
164
BOOL has_dmarc_record = TRUE;
 
165
u_char **ruf; /* forensic report addressees, if called for */
166
166
 
167
 
  /* ACLs have "control=dmarc_disable_verify" */
168
 
  if (dmarc_disable_verify == TRUE)
 
167
/* ACLs have "control=dmarc_disable_verify" */
 
168
if (dmarc_disable_verify)
169
169
  {
170
 
    dmarc_ar_header = dmarc_auth_results_header(from_header, NULL);
171
 
    return OK;
 
170
  dmarc_ar_header = dmarc_auth_results_header(from_header, NULL);
 
171
  return OK;
172
172
  }
173
173
 
174
 
  /* Store the header From: sender domain for this part of DMARC.
175
 
   * If there is no from_header struct, then it's likely this message
176
 
   * is locally generated and relying on fixups to add it.  Just skip
177
 
   * the entire DMARC system if we can't find a From: header....or if
178
 
   * there was a previous error.
179
 
   */
180
 
  if (from_header == NULL || dmarc_abort == TRUE)
181
 
    dmarc_abort = TRUE;
182
 
  else
 
174
/* Store the header From: sender domain for this part of DMARC.
 
175
 * If there is no from_header struct, then it's likely this message
 
176
 * is locally generated and relying on fixups to add it.  Just skip
 
177
 * the entire DMARC system if we can't find a From: header....or if
 
178
 * there was a previous error.
 
179
 */
 
180
if (!from_header || dmarc_abort)
 
181
  dmarc_abort = TRUE;
 
182
else
183
183
  {
184
184
    uschar * errormsg;
185
185
    int dummy, domain;
203
203
      opendmarc_policy_store_from_domain(dmarc_pctx, header_from_sender);
204
204
    if (libdm_status != DMARC_PARSE_OKAY)
205
205
    {
 
206
    log_write(0, LOG_MAIN|LOG_PANIC,
 
207
              "failure to store header From: in DMARC: %s, header was '%s'",
 
208
              opendmarc_policy_status_to_str(libdm_status), from_header->text);
 
209
    dmarc_abort = TRUE;
 
210
    }
 
211
  }
 
212
 
 
213
/* Skip DMARC if connection is SMTP Auth. Temporarily, admin should
 
214
 * instead do this in the ACLs.  */
 
215
if (!dmarc_abort && !sender_host_authenticated)
 
216
  {
 
217
  /* Use the envelope sender domain for this part of DMARC */
 
218
  spf_sender_domain = expand_string(US"$sender_address_domain");
 
219
  if (!spf_response)
 
220
    {
 
221
    /* No spf data means null envelope sender so generate a domain name
 
222
     * from the sender_helo_name  */
 
223
    if (!spf_sender_domain)
 
224
      {
 
225
      spf_sender_domain = sender_helo_name;
 
226
      log_write(0, LOG_MAIN, "DMARC using synthesized SPF sender domain = %s\n",
 
227
                             spf_sender_domain);
 
228
      DEBUG(D_receive)
 
229
        debug_printf("DMARC using synthesized SPF sender domain = %s\n",
 
230
          spf_sender_domain);
 
231
      }
 
232
    dmarc_spf_result = DMARC_POLICY_SPF_OUTCOME_NONE;
 
233
    dmarc_spf_ares_result = ARES_RESULT_UNKNOWN;
 
234
    origin = DMARC_POLICY_SPF_ORIGIN_HELO;
 
235
    spf_human_readable = US"";
 
236
    }
 
237
  else
 
238
    {
 
239
    sr = spf_response->result;
 
240
    dmarc_spf_result = sr == SPF_RESULT_NEUTRAL  ? DMARC_POLICY_SPF_OUTCOME_NONE :
 
241
                       sr == SPF_RESULT_PASS     ? DMARC_POLICY_SPF_OUTCOME_PASS :
 
242
                       sr == SPF_RESULT_FAIL     ? DMARC_POLICY_SPF_OUTCOME_FAIL :
 
243
                       sr == SPF_RESULT_SOFTFAIL ? DMARC_POLICY_SPF_OUTCOME_TMPFAIL :
 
244
                       DMARC_POLICY_SPF_OUTCOME_NONE;
 
245
    dmarc_spf_ares_result = sr == SPF_RESULT_NEUTRAL   ? ARES_RESULT_NEUTRAL :
 
246
                            sr == SPF_RESULT_PASS      ? ARES_RESULT_PASS :
 
247
                            sr == SPF_RESULT_FAIL      ? ARES_RESULT_FAIL :
 
248
                            sr == SPF_RESULT_SOFTFAIL  ? ARES_RESULT_SOFTFAIL :
 
249
                            sr == SPF_RESULT_NONE      ? ARES_RESULT_NONE :
 
250
                            sr == SPF_RESULT_TEMPERROR ? ARES_RESULT_TEMPERROR :
 
251
                            sr == SPF_RESULT_PERMERROR ? ARES_RESULT_PERMERROR :
 
252
                            ARES_RESULT_UNKNOWN;
 
253
    origin = DMARC_POLICY_SPF_ORIGIN_MAILFROM;
 
254
    spf_human_readable = (uschar *)spf_response->header_comment;
 
255
    DEBUG(D_receive)
 
256
      debug_printf("DMARC using SPF sender domain = %s\n", spf_sender_domain);
 
257
    }
 
258
  if (strcmp( CCS spf_sender_domain, "") == 0)
 
259
    dmarc_abort = TRUE;
 
260
  if (!dmarc_abort)
 
261
    {
 
262
    libdm_status = opendmarc_policy_store_spf(dmarc_pctx, spf_sender_domain,
 
263
                                dmarc_spf_result, origin, spf_human_readable);
 
264
    if (libdm_status != DMARC_PARSE_OKAY)
 
265
      log_write(0, LOG_MAIN|LOG_PANIC, "failure to store spf for DMARC: %s",
 
266
                           opendmarc_policy_status_to_str(libdm_status));
 
267
    }
 
268
 
 
269
  /* Now we cycle through the dkim signature results and put into
 
270
   * the opendmarc context, further building the DMARC reply.  */
 
271
  sig = dkim_signatures;
 
272
  dkim_history_buffer = US"";
 
273
  while (sig)
 
274
    {
 
275
    int dkim_result, dkim_ares_result, vs, ves;
 
276
    vs  = sig->verify_status;
 
277
    ves = sig->verify_ext_status;
 
278
    dkim_result = vs == PDKIM_VERIFY_PASS ? DMARC_POLICY_DKIM_OUTCOME_PASS :
 
279
                  vs == PDKIM_VERIFY_FAIL ? DMARC_POLICY_DKIM_OUTCOME_FAIL :
 
280
                  vs == PDKIM_VERIFY_INVALID ? DMARC_POLICY_DKIM_OUTCOME_TMPFAIL :
 
281
                  DMARC_POLICY_DKIM_OUTCOME_NONE;
 
282
    libdm_status = opendmarc_policy_store_dkim(dmarc_pctx, (uschar *)sig->domain,
 
283
                                               dkim_result, US"");
 
284
    DEBUG(D_receive)
 
285
      debug_printf("DMARC adding DKIM sender domain = %s\n", sig->domain);
 
286
    if (libdm_status != DMARC_PARSE_OKAY)
206
287
      log_write(0, LOG_MAIN|LOG_PANIC,
207
 
                "failure to store header From: in DMARC: %s, header was '%s'",
208
 
                opendmarc_policy_status_to_str(libdm_status), from_header->text);
209
 
      dmarc_abort = TRUE;
 
288
                "failure to store dkim (%s) for DMARC: %s",
 
289
                sig->domain, opendmarc_policy_status_to_str(libdm_status));
 
290
 
 
291
    dkim_ares_result =
 
292
      vs == PDKIM_VERIFY_PASS    ? ARES_RESULT_PASS :
 
293
      vs == PDKIM_VERIFY_FAIL    ? ARES_RESULT_FAIL :
 
294
      vs == PDKIM_VERIFY_NONE    ? ARES_RESULT_NONE :
 
295
      vs == PDKIM_VERIFY_INVALID ?
 
296
       ves == PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE ? ARES_RESULT_PERMERROR :
 
297
       ves == PDKIM_VERIFY_INVALID_BUFFER_SIZE        ? ARES_RESULT_PERMERROR :
 
298
       ves == PDKIM_VERIFY_INVALID_PUBKEY_PARSING     ? ARES_RESULT_PERMERROR :
 
299
       ARES_RESULT_UNKNOWN :
 
300
      ARES_RESULT_UNKNOWN;
 
301
    dkim_history_buffer = string_sprintf("%sdkim %s %d\n", dkim_history_buffer,
 
302
                                         sig->domain, dkim_ares_result);
 
303
    sig = sig->next;
210
304
    }
211
 
  }
212
 
 
213
 
  /* Skip DMARC if connection is SMTP Auth. Temporarily, admin should
214
 
   * instead do this in the ACLs.  */
215
 
  if (dmarc_abort == FALSE && sender_host_authenticated == NULL)
216
 
  {
217
 
    /* Use the envelope sender domain for this part of DMARC */
218
 
    spf_sender_domain = expand_string(US"$sender_address_domain");
219
 
    if ( spf_response == NULL )
 
305
  libdm_status = opendmarc_policy_query_dmarc(dmarc_pctx, US"");
 
306
  switch (libdm_status)
220
307
    {
221
 
      /* No spf data means null envelope sender so generate a domain name
222
 
       * from the sender_helo_name  */
223
 
      if (spf_sender_domain == NULL)
 
308
    case DMARC_DNS_ERROR_NXDOMAIN:
 
309
    case DMARC_DNS_ERROR_NO_RECORD:
 
310
      DEBUG(D_receive)
 
311
        debug_printf("DMARC no record found for %s\n", header_from_sender);
 
312
      has_dmarc_record = FALSE;
 
313
      break;
 
314
    case DMARC_PARSE_OKAY:
 
315
      DEBUG(D_receive)
 
316
        debug_printf("DMARC record found for %s\n", header_from_sender);
 
317
      break;
 
318
    case DMARC_PARSE_ERROR_BAD_VALUE:
 
319
      DEBUG(D_receive)
 
320
        debug_printf("DMARC record parse error for %s\n", header_from_sender);
 
321
      has_dmarc_record = FALSE;
 
322
      break;
 
323
    default:
 
324
      /* everything else, skip dmarc */
 
325
      DEBUG(D_receive)
 
326
        debug_printf("DMARC skipping (%d), unsure what to do with %s",
 
327
                      libdm_status, from_header->text);
 
328
      has_dmarc_record = FALSE;
 
329
      break;
 
330
    }
 
331
 
 
332
/* Store the policy string in an expandable variable. */
 
333
 
 
334
  libdm_status = opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans);
 
335
  for (c = 0; dmarc_policy_description[c].name; c++)
 
336
    if (tmp_ans == dmarc_policy_description[c].value)
224
337
      {
225
 
        spf_sender_domain = sender_helo_name;
226
 
        log_write(0, LOG_MAIN, "DMARC using synthesized SPF sender domain = %s\n",
227
 
                               spf_sender_domain);
228
 
        DEBUG(D_receive)
229
 
          debug_printf("DMARC using synthesized SPF sender domain = %s\n", spf_sender_domain);
230
 
      }
231
 
      dmarc_spf_result = DMARC_POLICY_SPF_OUTCOME_NONE;
232
 
      dmarc_spf_ares_result = ARES_RESULT_UNKNOWN;
233
 
      origin = DMARC_POLICY_SPF_ORIGIN_HELO;
234
 
      spf_human_readable = US"";
235
 
    }
236
 
    else
237
 
    {
238
 
      sr = spf_response->result;
239
 
      dmarc_spf_result = (sr == SPF_RESULT_NEUTRAL)  ? DMARC_POLICY_SPF_OUTCOME_NONE :
240
 
                         (sr == SPF_RESULT_PASS)     ? DMARC_POLICY_SPF_OUTCOME_PASS :
241
 
                         (sr == SPF_RESULT_FAIL)     ? DMARC_POLICY_SPF_OUTCOME_FAIL :
242
 
                         (sr == SPF_RESULT_SOFTFAIL) ? DMARC_POLICY_SPF_OUTCOME_TMPFAIL :
243
 
                         DMARC_POLICY_SPF_OUTCOME_NONE;
244
 
      dmarc_spf_ares_result = (sr == SPF_RESULT_NEUTRAL)   ? ARES_RESULT_NEUTRAL :
245
 
                              (sr == SPF_RESULT_PASS)      ? ARES_RESULT_PASS :
246
 
                              (sr == SPF_RESULT_FAIL)      ? ARES_RESULT_FAIL :
247
 
                              (sr == SPF_RESULT_SOFTFAIL)  ? ARES_RESULT_SOFTFAIL :
248
 
                              (sr == SPF_RESULT_NONE)      ? ARES_RESULT_NONE :
249
 
                              (sr == SPF_RESULT_TEMPERROR) ? ARES_RESULT_TEMPERROR :
250
 
                              (sr == SPF_RESULT_PERMERROR) ? ARES_RESULT_PERMERROR :
251
 
                              ARES_RESULT_UNKNOWN;
252
 
      origin = DMARC_POLICY_SPF_ORIGIN_MAILFROM;
253
 
      spf_human_readable = (uschar *)spf_response->header_comment;
254
 
      DEBUG(D_receive)
255
 
        debug_printf("DMARC using SPF sender domain = %s\n", spf_sender_domain);
256
 
    }
257
 
    if (strcmp( CCS spf_sender_domain, "") == 0)
258
 
      dmarc_abort = TRUE;
259
 
    if (dmarc_abort == FALSE)
260
 
    {
261
 
      libdm_status = opendmarc_policy_store_spf(dmarc_pctx, spf_sender_domain,
262
 
                                                dmarc_spf_result, origin, spf_human_readable);
263
 
      if (libdm_status != DMARC_PARSE_OKAY)
264
 
        log_write(0, LOG_MAIN|LOG_PANIC, "failure to store spf for DMARC: %s",
265
 
                             opendmarc_policy_status_to_str(libdm_status));
266
 
    }
267
 
 
268
 
    /* Now we cycle through the dkim signature results and put into
269
 
     * the opendmarc context, further building the DMARC reply.  */
270
 
    sig = dkim_signatures;
271
 
    dkim_history_buffer = US"";
272
 
    while (sig != NULL)
273
 
    {
274
 
      int dkim_result, dkim_ares_result, vs, ves;
275
 
      vs  = sig->verify_status;
276
 
      ves = sig->verify_ext_status;
277
 
      dkim_result = ( vs == PDKIM_VERIFY_PASS ) ? DMARC_POLICY_DKIM_OUTCOME_PASS :
278
 
                    ( vs == PDKIM_VERIFY_FAIL ) ? DMARC_POLICY_DKIM_OUTCOME_FAIL :
279
 
                    ( vs == PDKIM_VERIFY_INVALID ) ? DMARC_POLICY_DKIM_OUTCOME_TMPFAIL :
280
 
                    DMARC_POLICY_DKIM_OUTCOME_NONE;
281
 
      libdm_status = opendmarc_policy_store_dkim(dmarc_pctx, (uschar *)sig->domain,
282
 
                                                 dkim_result, US"");
283
 
      DEBUG(D_receive)
284
 
        debug_printf("DMARC adding DKIM sender domain = %s\n", sig->domain);
285
 
      if (libdm_status != DMARC_PARSE_OKAY)
286
 
        log_write(0, LOG_MAIN|LOG_PANIC, "failure to store dkim (%s) for DMARC: %s",
287
 
                  sig->domain, opendmarc_policy_status_to_str(libdm_status));
288
 
 
289
 
      dkim_ares_result = ( vs == PDKIM_VERIFY_PASS )    ? ARES_RESULT_PASS :
290
 
                         ( vs == PDKIM_VERIFY_FAIL )    ? ARES_RESULT_FAIL :
291
 
                         ( vs == PDKIM_VERIFY_NONE )    ? ARES_RESULT_NONE :
292
 
                         ( vs == PDKIM_VERIFY_INVALID ) ?
293
 
                           ( ves == PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE ? ARES_RESULT_PERMERROR :
294
 
                             ves == PDKIM_VERIFY_INVALID_BUFFER_SIZE        ? ARES_RESULT_PERMERROR :
295
 
                             ves == PDKIM_VERIFY_INVALID_PUBKEY_PARSING     ? ARES_RESULT_PERMERROR :
296
 
                             ARES_RESULT_UNKNOWN ) :
297
 
                          ARES_RESULT_UNKNOWN;
298
 
      dkim_history_buffer = string_sprintf("%sdkim %s %d\n", dkim_history_buffer,
299
 
                                           sig->domain, dkim_ares_result);
300
 
      sig = sig->next;
301
 
    }
302
 
    libdm_status = opendmarc_policy_query_dmarc(dmarc_pctx, US"");
303
 
    switch (libdm_status)
304
 
    {
305
 
      case DMARC_DNS_ERROR_NXDOMAIN:
306
 
      case DMARC_DNS_ERROR_NO_RECORD:
307
 
        DEBUG(D_receive)
308
 
          debug_printf("DMARC no record found for %s\n", header_from_sender);
309
 
        has_dmarc_record = FALSE;
310
 
        break;
311
 
      case DMARC_PARSE_OKAY:
312
 
        DEBUG(D_receive)
313
 
          debug_printf("DMARC record found for %s\n", header_from_sender);
314
 
        break;
315
 
      case DMARC_PARSE_ERROR_BAD_VALUE:
316
 
        DEBUG(D_receive)
317
 
          debug_printf("DMARC record parse error for %s\n", header_from_sender);
318
 
        has_dmarc_record = FALSE;
319
 
        break;
320
 
      default:
321
 
        /* everything else, skip dmarc */
322
 
        DEBUG(D_receive)
323
 
          debug_printf("DMARC skipping (%d), unsure what to do with %s",
324
 
                        libdm_status, from_header->text);
325
 
        has_dmarc_record = FALSE;
326
 
        break;
327
 
    }
328
 
 
329
 
  /* Store the policy string in an expandable variable. */
330
 
    libdm_status = opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans);
331
 
    for (c=0; dmarc_policy_description[c].name != NULL; c++) {
332
 
      if (tmp_ans == dmarc_policy_description[c].value) {
333
 
        dmarc_domain_policy = string_sprintf("%s",dmarc_policy_description[c].name);
334
 
        break;
335
 
      }
336
 
    }
337
 
 
338
 
    /* Can't use exim's string manipulation functions so allocate memory
339
 
     * for libopendmarc using its max hostname length definition. */
340
 
    uschar *dmarc_domain = (uschar *)calloc(DMARC_MAXHOSTNAMELEN, sizeof(uschar));
341
 
    libdm_status = opendmarc_policy_fetch_utilized_domain(dmarc_pctx, dmarc_domain,
342
 
                                                  DMARC_MAXHOSTNAMELEN-1);
343
 
    dmarc_used_domain = string_copy(dmarc_domain);
344
 
    free(dmarc_domain);
345
 
    if (libdm_status != DMARC_PARSE_OKAY)
346
 
    {
347
 
      log_write(0, LOG_MAIN|LOG_PANIC, "failure to read domainname used for DMARC lookup: %s",
348
 
                                       opendmarc_policy_status_to_str(libdm_status));
349
 
    }
350
 
    libdm_status = opendmarc_get_policy_to_enforce(dmarc_pctx);
351
 
    dmarc_policy = libdm_status;
352
 
    switch(libdm_status)
353
 
    {
354
 
      case DMARC_POLICY_ABSENT:     /* No DMARC record found */
355
 
        dmarc_status = US"norecord";
356
 
        dmarc_pass_fail = US"none";
357
 
        dmarc_status_text = US"No DMARC record";
358
 
        action = DMARC_RESULT_ACCEPT;
359
 
        break;
360
 
      case DMARC_FROM_DOMAIN_ABSENT:    /* No From: domain */
361
 
        dmarc_status = US"nofrom";
362
 
        dmarc_pass_fail = US"temperror";
363
 
        dmarc_status_text = US"No From: domain found";
364
 
        action = DMARC_RESULT_ACCEPT;
365
 
        break;
366
 
      case DMARC_POLICY_NONE:       /* Accept and report */
367
 
        dmarc_status = US"none";
368
 
        dmarc_pass_fail = US"none";
369
 
        dmarc_status_text = US"None, Accept";
370
 
        action = DMARC_RESULT_ACCEPT;
371
 
        break;
372
 
      case DMARC_POLICY_PASS:       /* Explicit accept */
373
 
        dmarc_status = US"accept";
374
 
        dmarc_pass_fail = US"pass";
375
 
        dmarc_status_text = US"Accept";
376
 
        action = DMARC_RESULT_ACCEPT;
377
 
        break;
378
 
      case DMARC_POLICY_REJECT:       /* Explicit reject */
379
 
        dmarc_status = US"reject";
380
 
        dmarc_pass_fail = US"fail";
381
 
        dmarc_status_text = US"Reject";
382
 
        action = DMARC_RESULT_REJECT;
383
 
        break;
384
 
      case DMARC_POLICY_QUARANTINE:       /* Explicit quarantine */
385
 
        dmarc_status = US"quarantine";
386
 
        dmarc_pass_fail = US"fail";
387
 
        dmarc_status_text = US"Quarantine";
388
 
        action = DMARC_RESULT_QUARANTINE;
389
 
        break;
390
 
      default:
391
 
        dmarc_status = US"temperror";
392
 
        dmarc_pass_fail = US"temperror";
393
 
        dmarc_status_text = US"Internal Policy Error";
394
 
        action = DMARC_RESULT_TEMPFAIL;
395
 
        break;
396
 
    }
397
 
 
398
 
    libdm_status = opendmarc_policy_fetch_alignment(dmarc_pctx, &da, &sa);
399
 
    if (libdm_status != DMARC_PARSE_OKAY)
400
 
    {
401
 
      log_write(0, LOG_MAIN|LOG_PANIC, "failure to read DMARC alignment: %s",
402
 
                                       opendmarc_policy_status_to_str(libdm_status));
403
 
    }
404
 
 
405
 
    if (has_dmarc_record == TRUE)
406
 
    {
407
 
      log_write(0, LOG_MAIN, "DMARC results: spf_domain=%s dmarc_domain=%s "
408
 
                             "spf_align=%s dkim_align=%s enforcement='%s'",
409
 
                             spf_sender_domain, dmarc_used_domain,
410
 
                             (sa==DMARC_POLICY_SPF_ALIGNMENT_PASS) ?"yes":"no",
411
 
                             (da==DMARC_POLICY_DKIM_ALIGNMENT_PASS)?"yes":"no",
412
 
                             dmarc_status_text);
413
 
      history_file_status = dmarc_write_history_file();
414
 
      /* Now get the forensic reporting addresses, if any */
415
 
      ruf = opendmarc_policy_fetch_ruf(dmarc_pctx, NULL, 0, 1);
416
 
      dmarc_send_forensic_report(ruf);
417
 
    }
418
 
  }
419
 
 
420
 
  /* set some global variables here */
421
 
  dmarc_ar_header = dmarc_auth_results_header(from_header, NULL);
422
 
 
423
 
  /* shut down libopendmarc */
424
 
  if ( dmarc_pctx != NULL )
425
 
    (void) opendmarc_policy_connect_shutdown(dmarc_pctx);
426
 
  if ( dmarc_disable_verify == FALSE )
427
 
    (void) opendmarc_policy_library_shutdown(&dmarc_ctx);
428
 
 
429
 
  return OK;
430
 
}
431
 
 
432
 
int dmarc_write_history_file()
433
 
{
434
 
  int history_file_fd;
435
 
  ssize_t written_len;
436
 
  int tmp_ans;
437
 
  u_char **rua; /* aggregate report addressees */
438
 
  uschar *history_buffer = NULL;
439
 
 
440
 
  if (dmarc_history_file == NULL)
441
 
    return DMARC_HIST_DISABLED;
442
 
  history_file_fd = log_create(dmarc_history_file);
443
 
 
444
 
  if (history_file_fd < 0)
445
 
  {
446
 
    log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s",
447
 
                             dmarc_history_file);
448
 
    return DMARC_HIST_FILE_ERR;
449
 
  }
450
 
 
451
 
  /* Generate the contents of the history file */
452
 
  history_buffer = string_sprintf("job %s\n", message_id);
453
 
  history_buffer = string_sprintf("%sreporter %s\n", history_buffer, primary_hostname);
454
 
  history_buffer = string_sprintf("%sreceived " TIME_T_FMT "\n", history_buffer, time(NULL));
455
 
  history_buffer = string_sprintf("%sipaddr %s\n", history_buffer, sender_host_address);
456
 
  history_buffer = string_sprintf("%sfrom %s\n", history_buffer, header_from_sender);
457
 
  history_buffer = string_sprintf("%smfrom %s\n", history_buffer,
458
 
                     expand_string(US"$sender_address_domain"));
459
 
 
460
 
  if (spf_response != NULL)
461
 
    history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result);
462
 
    /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */
463
 
 
464
 
  history_buffer = string_sprintf("%s%s", history_buffer, dkim_history_buffer);
465
 
  history_buffer = string_sprintf("%spdomain %s\n", history_buffer, dmarc_used_domain);
466
 
  history_buffer = string_sprintf("%spolicy %d\n", history_buffer, dmarc_policy);
467
 
 
468
 
  rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1);
469
 
  if (rua != NULL)
470
 
  {
471
 
    for (tmp_ans = 0; rua[tmp_ans] != NULL; tmp_ans++)
472
 
    {
473
 
      history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]);
474
 
    }
475
 
  }
476
 
  else
477
 
    history_buffer = string_sprintf("%srua -\n", history_buffer);
478
 
 
479
 
  opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans);
480
 
  history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans);
481
 
 
482
 
  opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans);
483
 
  history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans);
484
 
 
485
 
  opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans);
486
 
  history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans);
487
 
 
488
 
  opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans);
489
 
  history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans);
490
 
 
491
 
  opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans);
492
 
  history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans);
493
 
 
494
 
  history_buffer = string_sprintf("%salign_dkim %d\n", history_buffer, da);
495
 
  history_buffer = string_sprintf("%salign_spf %d\n", history_buffer, sa);
496
 
  history_buffer = string_sprintf("%saction %d\n", history_buffer, action);
497
 
 
498
 
  /* Write the contents to the history file */
 
338
      dmarc_domain_policy = string_sprintf("%s",dmarc_policy_description[c].name);
 
339
      break;
 
340
      }
 
341
 
 
342
  /* Can't use exim's string manipulation functions so allocate memory
 
343
   * for libopendmarc using its max hostname length definition. */
 
344
 
 
345
  uschar *dmarc_domain = (uschar *)calloc(DMARC_MAXHOSTNAMELEN, sizeof(uschar));
 
346
  libdm_status = opendmarc_policy_fetch_utilized_domain(dmarc_pctx,
 
347
    dmarc_domain, DMARC_MAXHOSTNAMELEN-1);
 
348
  dmarc_used_domain = string_copy(dmarc_domain);
 
349
  free(dmarc_domain);
 
350
 
 
351
  if (libdm_status != DMARC_PARSE_OKAY)
 
352
    log_write(0, LOG_MAIN|LOG_PANIC,
 
353
      "failure to read domainname used for DMARC lookup: %s",
 
354
      opendmarc_policy_status_to_str(libdm_status));
 
355
 
 
356
  libdm_status = opendmarc_get_policy_to_enforce(dmarc_pctx);
 
357
  dmarc_policy = libdm_status;
 
358
  switch(libdm_status)
 
359
    {
 
360
    case DMARC_POLICY_ABSENT:     /* No DMARC record found */
 
361
      dmarc_status = US"norecord";
 
362
      dmarc_pass_fail = US"none";
 
363
      dmarc_status_text = US"No DMARC record";
 
364
      action = DMARC_RESULT_ACCEPT;
 
365
      break;
 
366
    case DMARC_FROM_DOMAIN_ABSENT:    /* No From: domain */
 
367
      dmarc_status = US"nofrom";
 
368
      dmarc_pass_fail = US"temperror";
 
369
      dmarc_status_text = US"No From: domain found";
 
370
      action = DMARC_RESULT_ACCEPT;
 
371
      break;
 
372
    case DMARC_POLICY_NONE:       /* Accept and report */
 
373
      dmarc_status = US"none";
 
374
      dmarc_pass_fail = US"none";
 
375
      dmarc_status_text = US"None, Accept";
 
376
      action = DMARC_RESULT_ACCEPT;
 
377
      break;
 
378
    case DMARC_POLICY_PASS:       /* Explicit accept */
 
379
      dmarc_status = US"accept";
 
380
      dmarc_pass_fail = US"pass";
 
381
      dmarc_status_text = US"Accept";
 
382
      action = DMARC_RESULT_ACCEPT;
 
383
      break;
 
384
    case DMARC_POLICY_REJECT:       /* Explicit reject */
 
385
      dmarc_status = US"reject";
 
386
      dmarc_pass_fail = US"fail";
 
387
      dmarc_status_text = US"Reject";
 
388
      action = DMARC_RESULT_REJECT;
 
389
      break;
 
390
    case DMARC_POLICY_QUARANTINE:       /* Explicit quarantine */
 
391
      dmarc_status = US"quarantine";
 
392
      dmarc_pass_fail = US"fail";
 
393
      dmarc_status_text = US"Quarantine";
 
394
      action = DMARC_RESULT_QUARANTINE;
 
395
      break;
 
396
    default:
 
397
      dmarc_status = US"temperror";
 
398
      dmarc_pass_fail = US"temperror";
 
399
      dmarc_status_text = US"Internal Policy Error";
 
400
      action = DMARC_RESULT_TEMPFAIL;
 
401
      break;
 
402
    }
 
403
 
 
404
  libdm_status = opendmarc_policy_fetch_alignment(dmarc_pctx, &da, &sa);
 
405
  if (libdm_status != DMARC_PARSE_OKAY)
 
406
    log_write(0, LOG_MAIN|LOG_PANIC, "failure to read DMARC alignment: %s",
 
407
                             opendmarc_policy_status_to_str(libdm_status));
 
408
 
 
409
  if (has_dmarc_record == TRUE)
 
410
    {
 
411
    log_write(0, LOG_MAIN, "DMARC results: spf_domain=%s dmarc_domain=%s "
 
412
                           "spf_align=%s dkim_align=%s enforcement='%s'",
 
413
                           spf_sender_domain, dmarc_used_domain,
 
414
                           (sa==DMARC_POLICY_SPF_ALIGNMENT_PASS) ?"yes":"no",
 
415
                           (da==DMARC_POLICY_DKIM_ALIGNMENT_PASS)?"yes":"no",
 
416
                           dmarc_status_text);
 
417
    history_file_status = dmarc_write_history_file();
 
418
    /* Now get the forensic reporting addresses, if any */
 
419
    ruf = opendmarc_policy_fetch_ruf(dmarc_pctx, NULL, 0, 1);
 
420
    dmarc_send_forensic_report(ruf);
 
421
    }
 
422
  }
 
423
 
 
424
/* set some global variables here */
 
425
dmarc_ar_header = dmarc_auth_results_header(from_header, NULL);
 
426
 
 
427
/* shut down libopendmarc */
 
428
if ( dmarc_pctx != NULL )
 
429
  (void) opendmarc_policy_connect_shutdown(dmarc_pctx);
 
430
if ( dmarc_disable_verify == FALSE )
 
431
  (void) opendmarc_policy_library_shutdown(&dmarc_ctx);
 
432
 
 
433
return OK;
 
434
}
 
435
 
 
436
int
 
437
dmarc_write_history_file()
 
438
{
 
439
int history_file_fd;
 
440
ssize_t written_len;
 
441
int tmp_ans;
 
442
u_char **rua; /* aggregate report addressees */
 
443
uschar *history_buffer = NULL;
 
444
 
 
445
if (!dmarc_history_file)
 
446
  return DMARC_HIST_DISABLED;
 
447
history_file_fd = log_create(dmarc_history_file);
 
448
 
 
449
if (history_file_fd < 0)
 
450
{
 
451
  log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s",
 
452
                           dmarc_history_file);
 
453
  return DMARC_HIST_FILE_ERR;
 
454
}
 
455
 
 
456
/* Generate the contents of the history file */
 
457
history_buffer = string_sprintf(
 
458
  "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n",
 
459
  message_id, primary_hostname, time(NULL), sender_host_address,
 
460
  header_from_sender, expand_string(US"$sender_address_domain"));
 
461
 
 
462
if (spf_response)
 
463
  history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result);
 
464
  /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */
 
465
 
 
466
history_buffer = string_sprintf(
 
467
  "%s%spdomain %s\npolicy %d\n",
 
468
  history_buffer, dkim_history_buffer, dmarc_used_domain, dmarc_policy);
 
469
 
 
470
if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1)))
 
471
  for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++)
 
472
    history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]);
 
473
else
 
474
  history_buffer = string_sprintf("%srua -\n", history_buffer);
 
475
 
 
476
opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans);
 
477
history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans);
 
478
 
 
479
opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans);
 
480
history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans);
 
481
 
 
482
opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans);
 
483
history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans);
 
484
 
 
485
opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans);
 
486
history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans);
 
487
 
 
488
opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans);
 
489
history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans);
 
490
 
 
491
history_buffer = string_sprintf(
 
492
  "%salign_dkim %d\nalign_spf %d\naction %d\n",
 
493
  history_buffer, da, sa, action);
 
494
 
 
495
/* Write the contents to the history file */
 
496
DEBUG(D_receive)
 
497
  debug_printf("DMARC logging history data for opendmarc reporting%s\n",
 
498
             (host_checking || running_in_test_harness) ? " (not really)" : "");
 
499
if (host_checking || running_in_test_harness)
 
500
  {
499
501
  DEBUG(D_receive)
500
 
    debug_printf("DMARC logging history data for opendmarc reporting%s\n",
501
 
                 (host_checking || running_in_test_harness) ? " (not really)" : "");
502
 
  if (host_checking || running_in_test_harness)
503
 
  {
504
 
    DEBUG(D_receive)
505
 
      debug_printf("DMARC history data for debugging:\n%s", history_buffer);
 
502
    debug_printf("DMARC history data for debugging:\n%s", history_buffer);
506
503
  }
507
 
  else
 
504
else
508
505
  {
509
 
    written_len = write_to_fd_buf(history_file_fd,
510
 
                                  history_buffer,
511
 
                                  Ustrlen(history_buffer));
512
 
    if (written_len == 0)
 
506
  written_len = write_to_fd_buf(history_file_fd,
 
507
                                history_buffer,
 
508
                                Ustrlen(history_buffer));
 
509
  if (written_len == 0)
513
510
    {
514
 
      log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s",
515
 
                             dmarc_history_file);
516
 
      return DMARC_HIST_WRITE_ERR;
 
511
    log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s",
 
512
                           dmarc_history_file);
 
513
    return DMARC_HIST_WRITE_ERR;
517
514
    }
518
 
    (void)close(history_file_fd);
 
515
  (void)close(history_file_fd);
519
516
  }
520
 
  return DMARC_HIST_OK;
 
517
return DMARC_HIST_OK;
521
518
}
522
519
 
523
 
void dmarc_send_forensic_report(u_char **ruf)
 
520
void
 
521
dmarc_send_forensic_report(u_char **ruf)
524
522
{
525
 
  int   c;
526
 
  uschar *recipient, *save_sender;
527
 
  BOOL  send_status = FALSE;
528
 
  error_block *eblock = NULL;
529
 
  FILE *message_file = NULL;
530
 
 
531
 
  /* Earlier ACL does not have *required* control=dmarc_enable_forensic */
532
 
  if (dmarc_enable_forensic == FALSE)
533
 
    return;
534
 
 
535
 
  if ((dmarc_policy == DMARC_POLICY_REJECT     && action == DMARC_RESULT_REJECT) ||
536
 
      (dmarc_policy == DMARC_POLICY_QUARANTINE && action == DMARC_RESULT_QUARANTINE) )
537
 
  {
538
 
    if (ruf != NULL)
 
523
int   c;
 
524
uschar *recipient, *save_sender;
 
525
BOOL  send_status = FALSE;
 
526
error_block *eblock = NULL;
 
527
FILE *message_file = NULL;
 
528
 
 
529
/* Earlier ACL does not have *required* control=dmarc_enable_forensic */
 
530
if (!dmarc_enable_forensic)
 
531
  return;
 
532
 
 
533
if ((dmarc_policy == DMARC_POLICY_REJECT     && action == DMARC_RESULT_REJECT) ||
 
534
    (dmarc_policy == DMARC_POLICY_QUARANTINE && action == DMARC_RESULT_QUARANTINE) )
 
535
  if (ruf)
539
536
    {
540
 
      eblock = add_to_eblock(eblock, US"Sender Domain", dmarc_used_domain);
541
 
      eblock = add_to_eblock(eblock, US"Sender IP Address", sender_host_address);
542
 
      eblock = add_to_eblock(eblock, US"Received Date", tod_stamp(tod_full));
543
 
      eblock = add_to_eblock(eblock, US"SPF Alignment",
544
 
                             (sa==DMARC_POLICY_SPF_ALIGNMENT_PASS) ?US"yes":US"no");
545
 
      eblock = add_to_eblock(eblock, US"DKIM Alignment",
546
 
                             (da==DMARC_POLICY_DKIM_ALIGNMENT_PASS)?US"yes":US"no");
547
 
      eblock = add_to_eblock(eblock, US"DMARC Results", dmarc_status_text);
548
 
      /* Set a sane default envelope sender */
549
 
      dsn_from = dmarc_forensic_sender ? dmarc_forensic_sender :
550
 
                 dsn_from ? dsn_from :
551
 
                 string_sprintf("do-not-reply@%s",primary_hostname);
552
 
      for (c = 0; ruf[c] != NULL; c++)
 
537
    eblock = add_to_eblock(eblock, US"Sender Domain", dmarc_used_domain);
 
538
    eblock = add_to_eblock(eblock, US"Sender IP Address", sender_host_address);
 
539
    eblock = add_to_eblock(eblock, US"Received Date", tod_stamp(tod_full));
 
540
    eblock = add_to_eblock(eblock, US"SPF Alignment",
 
541
                           (sa==DMARC_POLICY_SPF_ALIGNMENT_PASS) ?US"yes":US"no");
 
542
    eblock = add_to_eblock(eblock, US"DKIM Alignment",
 
543
                           (da==DMARC_POLICY_DKIM_ALIGNMENT_PASS)?US"yes":US"no");
 
544
    eblock = add_to_eblock(eblock, US"DMARC Results", dmarc_status_text);
 
545
    /* Set a sane default envelope sender */
 
546
    dsn_from = dmarc_forensic_sender ? dmarc_forensic_sender :
 
547
               dsn_from ? dsn_from :
 
548
               string_sprintf("do-not-reply@%s",primary_hostname);
 
549
    for (c = 0; ruf[c]; c++)
553
550
      {
554
 
        recipient = string_copylc(ruf[c]);
555
 
        if (Ustrncmp(recipient, "mailto:",7))
556
 
        continue;
557
 
        /* Move to first character past the colon */
558
 
        recipient += 7;
559
 
        DEBUG(D_receive)
560
 
          debug_printf("DMARC forensic report to %s%s\n", recipient,
561
 
                       (host_checking || running_in_test_harness) ? " (not really)" : "");
562
 
        if (host_checking || running_in_test_harness)
563
 
          continue;
564
 
        save_sender = sender_address;
565
 
        sender_address = recipient;
566
 
        send_status = moan_to_sender(ERRMESS_DMARC_FORENSIC, eblock,
567
 
                                     header_list, message_file, FALSE);
568
 
        sender_address = save_sender;
569
 
        if (send_status == FALSE)
570
 
          log_write(0, LOG_MAIN|LOG_PANIC, "failure to send DMARC forensic report to %s",
571
 
                    recipient);
 
551
      recipient = string_copylc(ruf[c]);
 
552
      if (Ustrncmp(recipient, "mailto:",7))
 
553
        continue;
 
554
      /* Move to first character past the colon */
 
555
      recipient += 7;
 
556
      DEBUG(D_receive)
 
557
        debug_printf("DMARC forensic report to %s%s\n", recipient,
 
558
             (host_checking || running_in_test_harness) ? " (not really)" : "");
 
559
      if (host_checking || running_in_test_harness)
 
560
        continue;
 
561
 
 
562
      save_sender = sender_address;
 
563
      sender_address = recipient;
 
564
      send_status = moan_to_sender(ERRMESS_DMARC_FORENSIC, eblock,
 
565
                                   header_list, message_file, FALSE);
 
566
      sender_address = save_sender;
 
567
      if (!send_status)
 
568
        log_write(0, LOG_MAIN|LOG_PANIC,
 
569
          "failure to send DMARC forensic report to %s", recipient);
572
570
      }
573
571
    }
574
 
  }
575
 
}
576
 
 
577
 
uschar *dmarc_exim_expand_query(int what)
578
 
{
579
 
  if (dmarc_disable_verify || !dmarc_pctx)
580
 
    return dmarc_exim_expand_defaults(what);
581
 
 
582
 
  switch(what) {
583
 
    case DMARC_VERIFY_STATUS:
584
 
      return(dmarc_status);
585
 
    default:
586
 
      return US"";
587
 
  }
588
 
}
589
 
 
590
 
uschar *dmarc_exim_expand_defaults(int what)
591
 
{
592
 
  switch(what) {
593
 
    case DMARC_VERIFY_STATUS:
594
 
      return (dmarc_disable_verify) ?
595
 
              US"off" :
596
 
              US"none";
597
 
    default:
598
 
      return US"";
599
 
  }
600
 
}
601
 
 
602
 
uschar *dmarc_auth_results_header(header_line *from_header, uschar *hostname)
603
 
{
604
 
  uschar *hdr_tmp    = US"";
605
 
 
606
 
  /* Allow a server hostname to be passed to this function, but is
607
 
   * currently unused */
608
 
  if (hostname == NULL)
609
 
    hostname = primary_hostname;
610
 
  hdr_tmp = string_sprintf("%s %s;", DMARC_AR_HEADER, hostname);
 
572
}
 
573
 
 
574
uschar *
 
575
dmarc_exim_expand_query(int what)
 
576
{
 
577
if (dmarc_disable_verify || !dmarc_pctx)
 
578
  return dmarc_exim_expand_defaults(what);
 
579
 
 
580
switch(what)
 
581
  {
 
582
  case DMARC_VERIFY_STATUS:
 
583
    return(dmarc_status);
 
584
  default:
 
585
    return US"";
 
586
  }
 
587
}
 
588
 
 
589
uschar *
 
590
dmarc_exim_expand_defaults(int what)
 
591
{
 
592
switch(what)
 
593
  {
 
594
  case DMARC_VERIFY_STATUS:
 
595
    return dmarc_disable_verify ?  US"off" : US"none";
 
596
  default:
 
597
    return US"";
 
598
  }
 
599
}
 
600
 
 
601
uschar *
 
602
dmarc_auth_results_header(header_line *from_header, uschar *hostname)
 
603
{
 
604
uschar *hdr_tmp    = US"";
 
605
 
 
606
/* Allow a server hostname to be passed to this function, but is
 
607
 * currently unused */
 
608
if (!hostname)
 
609
  hostname = primary_hostname;
 
610
hdr_tmp = string_sprintf("%s %s;", DMARC_AR_HEADER, hostname);
611
611
 
612
612
#if 0
613
 
  /* I don't think this belongs here, but left it here commented out
614
 
   * because it was a lot of work to get working right. */
615
 
  if (spf_response != NULL) {
616
 
    uschar *dmarc_ar_spf = US"";
617
 
    int sr               = 0;
618
 
    sr = spf_response->result;
619
 
    dmarc_ar_spf = (sr == SPF_RESULT_NEUTRAL)  ? US"neutral" :
620
 
                   (sr == SPF_RESULT_PASS)     ? US"pass" :
621
 
                   (sr == SPF_RESULT_FAIL)     ? US"fail" :
622
 
                   (sr == SPF_RESULT_SOFTFAIL) ? US"softfail" :
623
 
                   US"none";
624
 
    hdr_tmp = string_sprintf("%s spf=%s (%s) smtp.mail=%s;",
625
 
                             hdr_tmp, dmarc_ar_spf_result,
626
 
                             spf_response->header_comment,
627
 
                             expand_string(US"$sender_address") );
628
 
  }
 
613
/* I don't think this belongs here, but left it here commented out
 
614
 * because it was a lot of work to get working right. */
 
615
if (spf_response != NULL) {
 
616
  uschar *dmarc_ar_spf = US"";
 
617
  int sr               = 0;
 
618
  sr = spf_response->result;
 
619
  dmarc_ar_spf = (sr == SPF_RESULT_NEUTRAL)  ? US"neutral" :
 
620
                 (sr == SPF_RESULT_PASS)     ? US"pass" :
 
621
                 (sr == SPF_RESULT_FAIL)     ? US"fail" :
 
622
                 (sr == SPF_RESULT_SOFTFAIL) ? US"softfail" :
 
623
                 US"none";
 
624
  hdr_tmp = string_sprintf("%s spf=%s (%s) smtp.mail=%s;",
 
625
                           hdr_tmp, dmarc_ar_spf_result,
 
626
                           spf_response->header_comment,
 
627
                           expand_string(US"$sender_address") );
 
628
}
629
629
#endif
630
 
  hdr_tmp = string_sprintf("%s dmarc=%s",
631
 
                           hdr_tmp, dmarc_pass_fail);
632
 
  if (header_from_sender)
633
 
    hdr_tmp = string_sprintf("%s header.from=%s",
634
 
                             hdr_tmp, header_from_sender);
635
 
  return hdr_tmp;
 
630
 
 
631
hdr_tmp = string_sprintf("%s dmarc=%s", hdr_tmp, dmarc_pass_fail);
 
632
if (header_from_sender)
 
633
  hdr_tmp = string_sprintf("%s header.from=%s",
 
634
                           hdr_tmp, header_from_sender);
 
635
return hdr_tmp;
636
636
}
637
637
 
638
 
#endif /* EXPERIMENTAL_SPF */
 
638
# endif /* EXPERIMENTAL_SPF */
639
639
#endif /* EXPERIMENTAL_DMARC */
 
640
/* vi: aw ai sw=2
 
641
 */