114
105
#include <vstring.h>
115
106
#include <vstream.h>
116
107
#include <stringops.h>
117
#include <valid_hostname.h>
118
#include <name_code.h>
120
109
/* Global library. */
122
111
#include <mime_state.h>
123
112
#include <debug_peer.h>
124
113
#include <mail_params.h>
126
#include <smtp_stream.h>
128
115
/* Application-specific. */
130
117
#include "smtp.h"
131
118
#include "smtp_sasl.h"
135
static MAPS *tls_policy; /* lookup table(s) */
136
static MAPS *tls_per_site; /* lookup table(s) */
138
/* smtp_tls_list_init - initialize per-site policy lists */
140
void smtp_tls_list_init(void)
142
if (*var_smtp_tls_policy) {
143
tls_policy = maps_create(VAR_SMTP_TLS_POLICY, var_smtp_tls_policy,
144
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
145
if (*var_smtp_tls_per_site)
146
msg_warn("%s ignored when %s is not empty.",
147
VAR_SMTP_TLS_PER_SITE, VAR_SMTP_TLS_POLICY);
150
if (*var_smtp_tls_per_site) {
151
tls_per_site = maps_create(VAR_SMTP_TLS_PER_SITE, var_smtp_tls_per_site,
152
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
156
/* policy_name - printable tls policy level */
158
static const char *policy_name(int tls_level)
160
const char *name = str_tls_level(tls_level);
167
/* tls_site_lookup - look up per-site TLS security level */
169
static void tls_site_lookup(int *site_level, const char *site_name,
170
const char *site_class)
175
* Look up a non-default policy. In case of multiple lookup results, the
176
* precedence order is a permutation of the TLS enforcement level order:
177
* VERIFY, ENCRYPT, NONE, MAY, NOTFOUND. I.e. we override MAY with a more
178
* specific policy including NONE, otherwise we choose the stronger
181
if ((lookup = maps_find(tls_per_site, site_name, 0)) != 0) {
182
if (!strcasecmp(lookup, "NONE")) {
183
/* NONE overrides MAY or NOTFOUND. */
184
if (*site_level <= TLS_LEV_MAY)
185
*site_level = TLS_LEV_NONE;
186
} else if (!strcasecmp(lookup, "MAY")) {
187
/* MAY overrides NOTFOUND but not NONE. */
188
if (*site_level < TLS_LEV_NONE)
189
*site_level = TLS_LEV_MAY;
190
} else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) {
191
if (*site_level < TLS_LEV_ENCRYPT)
192
*site_level = TLS_LEV_ENCRYPT;
193
} else if (!strcasecmp(lookup, "MUST")) {
194
if (*site_level < TLS_LEV_VERIFY)
195
*site_level = TLS_LEV_VERIFY;
197
msg_warn("Table %s: ignoring unknown TLS policy '%s' for %s %s",
198
var_smtp_tls_per_site, lookup, site_class, site_name);
200
} else if (tls_per_site->error) {
201
msg_fatal("%s lookup error for %s", tls_per_site->title, site_name);
205
/* tls_policy_lookup_one - look up destination TLS policy */
207
static int tls_policy_lookup_one(SMTP_SESSION *session, int *site_level,
208
const char *site_name,
209
const char *site_class)
218
static VSTRING *cbuf;
221
#define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0)
223
if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
224
if (tls_policy->error) {
225
msg_fatal("%s: %s lookup error for %s",
226
session->state->request->queue_id,
227
tls_policy->title, site_name);
228
/* XXX session->stream has no longjmp context yet. */
233
cbuf = vstring_alloc(10);
236
vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \
237
site_class, site_name))
239
saved_policy = policy = mystrdup(lookup);
241
if ((tok = mystrtok(&policy, "\t\n\r ,")) == 0) {
242
msg_warn("%s: invalid empty policy", WHERE);
243
*site_level = TLS_LEV_INVALID;
244
FREE_RETURN(1); /* No further lookups */
246
*site_level = tls_level_lookup(tok);
247
if (*site_level == TLS_LEV_INVALID) {
248
/* tls_level_lookup() logs no warning. */
249
msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
250
FREE_RETURN(1); /* No further lookups */
254
* Warn about ignored attributes when TLS is disabled.
256
if (*site_level < TLS_LEV_MAY) {
257
while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0)
258
msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
264
* Errors in attributes may have security consequences, don't ignore
265
* errors that can degrade security.
267
while ((tok = mystrtok(&policy, "\t\n\r ,")) != 0) {
268
if ((err = split_nameval(tok, &name, &val)) != 0) {
269
*site_level = TLS_LEV_INVALID;
270
msg_warn("%s: malformed attribute/value pair \"%s\": %s",
274
/* Only one instance per policy. */
275
if (!strcasecmp(name, "ciphers")) {
277
msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
278
*site_level = TLS_LEV_INVALID;
281
if (session->tls_grade) {
282
msg_warn("%s: attribute \"%s\" is specified multiple times",
284
*site_level = TLS_LEV_INVALID;
287
session->tls_grade = mystrdup(val);
290
/* Only one instance per policy. */
291
if (!strcasecmp(name, "protocols")) {
292
if (session->tls_protocols) {
293
msg_warn("%s: attribute \"%s\" is specified multiple times",
295
*site_level = TLS_LEV_INVALID;
298
session->tls_protocols = mystrdup(val);
301
/* Multiple instance(s) per policy. */
302
if (!strcasecmp(name, "match")) {
303
char *delim = *site_level == TLS_LEV_FPRINT ? "|" : ":";
305
if (*site_level <= TLS_LEV_ENCRYPT) {
306
msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"",
307
WHERE, name, policy_name(*site_level));
308
*site_level = TLS_LEV_INVALID;
312
msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
313
*site_level = TLS_LEV_INVALID;
316
if (session->tls_matchargv == 0)
317
session->tls_matchargv = argv_split(val, delim);
319
argv_split_append(session->tls_matchargv, val, delim);
322
/* Only one instance per policy. */
323
if (!strcasecmp(name, "exclude")) {
324
if (session->tls_exclusions) {
325
msg_warn("%s: attribute \"%s\" is specified multiple times",
327
*site_level = TLS_LEV_INVALID;
330
session->tls_exclusions = vstring_strcpy(vstring_alloc(10), val);
333
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
334
*site_level = TLS_LEV_INVALID;
341
/* tls_policy_lookup - look up destination TLS policy */
343
static void tls_policy_lookup(SMTP_SESSION *session, int *site_level,
344
const char *site_name,
345
const char *site_class)
349
* Only one lookup with [nexthop]:port, [nexthop] or nexthop:port These
350
* are never the domain part of localpart@domain, rather they are
351
* explicit nexthops from transport:nexthop, and match only the
352
* corresponding policy. Parent domain matching (below) applies only to
353
* sub-domains of the recipient domain.
355
if (!valid_hostname(site_name, DONT_GRIPE)) {
356
tls_policy_lookup_one(session, site_level, site_name, site_class);
361
* XXX For clarity consider using ``do { .. } while'', instead of using
362
* ``while { .. }'' with loop control at the bottom.
365
/* Try the given domain */
366
if (tls_policy_lookup_one(session, site_level, site_name, site_class))
368
/* Re-try with parent domain */
369
if ((site_name = strchr(site_name + 1, '.')) == 0)
374
/* set_cipher_grade - Set cipher grade and exclusions */
376
static void set_cipher_grade(SMTP_SESSION *session)
378
const char *mand_exclude = "";
379
const char *also_exclude = "";
382
* Use main.cf cipher level if no per-destination value specified. With
383
* mandatory encryption at least encrypt, and with mandatory verification
384
* at least authenticate!
386
switch (session->tls_level) {
387
case TLS_LEV_INVALID:
392
if (session->tls_grade == 0)
393
session->tls_grade = mystrdup(var_smtp_tls_ciph);
396
case TLS_LEV_ENCRYPT:
397
if (session->tls_grade == 0)
398
session->tls_grade = mystrdup(var_smtp_tls_mand_ciph);
399
mand_exclude = var_smtp_tls_mand_excl;
400
also_exclude = "eNULL";
406
if (session->tls_grade == 0)
407
session->tls_grade = mystrdup(var_smtp_tls_mand_ciph);
408
mand_exclude = var_smtp_tls_mand_excl;
409
also_exclude = "aNULL";
413
#define ADD_EXCLUDE(vstr, str) \
416
vstring_sprintf_append((vstr), "%s%s", \
417
VSTRING_LEN(vstr) ? " " : "", (str)); \
421
* The "exclude" policy table attribute overrides main.cf exclusion
424
if (session->tls_exclusions == 0) {
425
session->tls_exclusions = vstring_alloc(10);
426
ADD_EXCLUDE(session->tls_exclusions, var_smtp_tls_excl_ciph);
427
ADD_EXCLUDE(session->tls_exclusions, mand_exclude);
429
ADD_EXCLUDE(session->tls_exclusions, also_exclude);
432
/* session_tls_init - session TLS parameters */
434
static void session_tls_init(SMTP_SESSION *session, const char *dest,
435
const char *host, int flags)
437
const char *myname = "session_tls_init";
442
* Initialize all TLS related session properties.
444
session->tls_context = 0;
445
session->tls_nexthop = 0;
446
session->tls_level = TLS_LEV_NONE;
447
session->tls_retry_plain = 0;
448
session->tls_protocols = 0;
449
session->tls_grade = 0;
450
session->tls_exclusions = 0;
451
session->tls_matchargv = 0;
454
* Compute the global TLS policy. This is the default policy level when
455
* no per-site policy exists. It also is used to override a wild-card
458
if (*var_smtp_tls_level) {
459
/* Require that var_smtp_tls_level is sanitized upon startup. */
460
global_level = tls_level_lookup(var_smtp_tls_level);
461
if (global_level == TLS_LEV_INVALID)
462
msg_panic("%s: invalid TLS security level: \"%s\"",
463
myname, var_smtp_tls_level);
464
} else if (var_smtp_enforce_tls) {
465
global_level = var_smtp_tls_enforce_peername ?
466
TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
468
global_level = var_smtp_use_tls ?
469
TLS_LEV_MAY : TLS_LEV_NONE;
472
msg_info("%s TLS level: %s", "global", policy_name(global_level));
475
* Compute the per-site TLS enforcement level. For compatibility with the
476
* original TLS patch, this algorithm is gives equal precedence to host
477
* and next-hop policies.
479
site_level = TLS_LEV_NOTFOUND;
482
tls_policy_lookup(session, &site_level, dest, "next-hop destination");
483
} else if (tls_per_site) {
484
tls_site_lookup(&site_level, dest, "next-hop destination");
485
if (strcasecmp(dest, host) != 0)
486
tls_site_lookup(&site_level, host, "server hostname");
488
msg_info("%s TLS level: %s", "site", policy_name(site_level));
491
* Override a wild-card per-site policy with a more specific global
494
* With the original TLS patch, 1) a per-site ENCRYPT could not override
495
* a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
496
* produced inconsistent results: it changed a global VERIFY into
497
* NONE, while producing MAY with all weaker global policy settings.
499
* With the current implementation, a combined per-site (NONE+MAY)
500
* consistently overrides global policy with NONE, and global policy
501
* can override only a per-site MAY wildcard. That is, specific
502
* policies consistently override wildcard policies, and
503
* (non-wildcard) per-site policies consistently override global
506
if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY)
507
site_level = global_level;
509
if (site_level == TLS_LEV_NOTFOUND)
510
session->tls_level = global_level;
512
session->tls_level = site_level;
515
* Use main.cf protocols setting if not set in per-destination table.
517
if (session->tls_level > TLS_LEV_NONE && session->tls_protocols == 0)
518
session->tls_protocols =
519
mystrdup((session->tls_level == TLS_LEV_MAY) ?
520
var_smtp_tls_proto : var_smtp_tls_mand_proto);
523
* Compute cipher grade (if set in per-destination table, else
524
* set_cipher() uses main.cf settings) and security level dependent
525
* cipher exclusion list.
527
set_cipher_grade(session);
530
* Use main.cf cert_match setting if not set in per-destination table.
532
if (session->tls_matchargv == 0) {
533
switch (session->tls_level) {
534
case TLS_LEV_INVALID:
537
case TLS_LEV_ENCRYPT:
540
session->tls_matchargv =
541
argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |");
544
session->tls_matchargv =
545
argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :");
548
session->tls_matchargv =
549
argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :");
552
msg_panic("unexpected TLS security level: %d",
556
if (msg_verbose && (tls_policy || tls_per_site))
557
msg_info("%s TLS level: %s", "effective",
558
policy_name(session->tls_level));
563
120
/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
565
SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, const char *dest,
566
const char *host, const char *addr,
567
unsigned port, time_t start,
122
SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
123
time_t start, int flags)
570
125
SMTP_SESSION *session;
126
const char *host = STR(iter->host);
127
const char *addr = STR(iter->addr);
128
unsigned port = iter->port;
572
130
session = (SMTP_SESSION *) mymalloc(sizeof(*session));
573
131
session->stream = stream;
574
session->dest = mystrdup(dest);
575
session->host = mystrdup(host);
576
session->addr = mystrdup(addr);
132
session->iterator = iter;
577
133
session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
578
134
session->helo = 0;
579
135
session->port = port;