1 /* $NetBSD: smtp_tls_policy.c,v 1.5 2025/02/25 19:15:49 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* smtp_tls_policy 3
6 /* SUMMARY
7 /* SMTP_TLS_POLICY structure management
8 /* SYNOPSIS
9 /* #include "smtp.h"
10 /*
11 /* void smtp_tls_list_init()
12 /*
13 /* int smtp_tls_policy_cache_query(why, tls, iter)
14 /* DSN_BUF *why;
15 /* SMTP_TLS_POLICY *tls;
16 /* SMTP_ITERATOR *iter;
17 /*
18 /* void smtp_tls_policy_dummy(tls)
19 /* SMTP_TLS_POLICY *tls;
20 /*
21 /* void smtp_tls_policy_cache_flush()
22 /* DESCRIPTION
23 /* smtp_tls_list_init() initializes lookup tables used by the TLS
24 /* policy engine.
25 /*
26 /* smtp_tls_policy_cache_query() returns a shallow copy of the
27 /* cached SMTP_TLS_POLICY structure for the iterator's
28 /* destination, host, port and DNSSEC validation status.
29 /* This copy is guaranteed to be valid until the next
30 /* smtp_tls_policy_cache_query() or smtp_tls_policy_cache_flush()
31 /* call. The caller can override the TLS security level without
32 /* corrupting the policy cache.
33 /* When any required table or DNS lookups fail, the TLS level
34 /* is set to TLS_LEV_INVALID, the "why" argument is updated
35 /* with the error reason and the result value is zero (false).
36 /*
37 /* smtp_tls_policy_dummy() initializes a trivial, non-cached,
38 /* policy with TLS disabled.
39 /*
40 /* smtp_tls_policy_cache_flush() destroys the TLS policy cache
41 /* and contents.
42 /*
43 /* Arguments:
44 /* .IP why
45 /* A pointer to a DSN_BUF which holds error status information when
46 /* the TLS policy lookup fails.
47 /* .IP tls
48 /* Pointer to TLS policy storage.
49 /* .IP iter
50 /* The literal next-hop or fall-back destination including
51 /* the optional [] and including the :port or :service;
52 /* the name of the remote host after MX and CNAME expansions
53 /* (see smtp_cname_overrides_servername for the handling
54 /* of hostnames that resolve to a CNAME record);
55 /* the printable address of the remote host;
56 /* the remote port in network byte order;
57 /* the DNSSEC validation status of the host name lookup after
58 /* MX and CNAME expansions.
59 /* LICENSE
60 /* .ad
61 /* .fi
62 /* This software is free. You can do with it whatever you want.
63 /* The original author kindly requests that you acknowledge
64 /* the use of his software.
65 /* AUTHOR(S)
66 /* TLS support originally by:
67 /* Lutz Jaenicke
68 /* BTU Cottbus
69 /* Allgemeine Elektrotechnik
70 /* Universitaetsplatz 3-4
71 /* D-03044 Cottbus, Germany
72 /*
73 /* Updated by:
74 /* Wietse Venema
75 /* IBM T.J. Watson Research
76 /* P.O. Box 704
77 /* Yorktown Heights, NY 10598, USA
78 /*
79 /* Wietse Venema
80 /* Google, Inc.
81 /* 111 8th Avenue
82 /* New York, NY 10011, USA
83 /*
84 /* Viktor Dukhovni
85 /*--*/
86
87 /* System library. */
88
89 #include <sys_defs.h>
90
91 #ifdef USE_TLS
92
93 #include <netinet/in.h> /* ntohs() for Solaris or BSD */
94 #include <arpa/inet.h> /* ntohs() for Linux or BSD */
95 #include <stdlib.h>
96 #include <string.h>
97
98 #ifdef STRCASECMP_IN_STRINGS_H
99 #include <strings.h>
100 #endif
101
102 /* Utility library. */
103
104 #include <msg.h>
105 #include <mymalloc.h>
106 #include <vstring.h>
107 #include <sane_strtol.h>
108 #include <stringops.h>
109 #include <valid_hostname.h>
110 #include <valid_utf8_hostname.h>
111 #include <ctable.h>
112
113 /* Global library. */
114
115 #include <mail_params.h>
116 #include <maps.h>
117 #include <dsn_buf.h>
118
119 /* TLS library. */
120
121 #include <tlsrpt_wrapper.h>
122
123 /* DNS library. */
124
125 #include <dns.h>
126
127 /* Application-specific. */
128
129 #include "smtp.h"
130
131 /* XXX Cache size should scale with [sl]mtp_mx_address_limit. */
132 #define CACHE_SIZE 20
133 static CTABLE *policy_cache;
134
135 static int global_tls_level(void);
136 static void dane_init(SMTP_TLS_POLICY *, SMTP_ITERATOR *);
137
138 static MAPS *tls_policy; /* lookup table(s) */
139 static MAPS *tls_per_site; /* lookup table(s) */
140
141 /* smtp_tls_list_init - initialize per-site policy lists */
142
smtp_tls_list_init(void)143 void smtp_tls_list_init(void)
144 {
145 if (*var_smtp_tls_policy) {
146 tls_policy = maps_create(VAR_LMTP_SMTP(TLS_POLICY),
147 var_smtp_tls_policy,
148 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
149 | DICT_FLAG_UTF8_REQUEST);
150 if (*var_smtp_tls_per_site)
151 msg_warn("%s ignored when %s is not empty.",
152 VAR_LMTP_SMTP(TLS_PER_SITE), VAR_LMTP_SMTP(TLS_POLICY));
153 return;
154 }
155 if (*var_smtp_tls_per_site) {
156 tls_per_site = maps_create(VAR_LMTP_SMTP(TLS_PER_SITE),
157 var_smtp_tls_per_site,
158 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
159 | DICT_FLAG_UTF8_REQUEST);
160 }
161 }
162
163 /* policy_name - printable tls policy level */
164
policy_name(int tls_level)165 static const char *policy_name(int tls_level)
166 {
167 const char *name = str_tls_level(tls_level);
168
169 if (name == 0)
170 name = "unknown";
171 return name;
172 }
173
174 #define MARK_INVALID(why, levelp) do { \
175 dsb_simple((why), "4.7.5", "client TLS configuration problem"); \
176 *(levelp) = TLS_LEV_INVALID; } while (0)
177
178 /* tls_site_lookup - look up per-site TLS security level */
179
tls_site_lookup(SMTP_TLS_POLICY * tls,int * site_level,const char * site_name,const char * site_class)180 static void tls_site_lookup(SMTP_TLS_POLICY *tls, int *site_level,
181 const char *site_name, const char *site_class)
182 {
183 const char *lookup;
184
185 /*
186 * Look up a non-default policy. In case of multiple lookup results, the
187 * precedence order is a permutation of the TLS enforcement level order:
188 * VERIFY, ENCRYPT, NONE, MAY, NOTFOUND. I.e. we override MAY with a more
189 * specific policy including NONE, otherwise we choose the stronger
190 * enforcement level.
191 */
192 if ((lookup = maps_find(tls_per_site, site_name, 0)) != 0) {
193 if (!strcasecmp(lookup, "NONE")) {
194 /* NONE overrides MAY or NOTFOUND. */
195 if (*site_level <= TLS_LEV_MAY)
196 *site_level = TLS_LEV_NONE;
197 } else if (!strcasecmp(lookup, "MAY")) {
198 /* MAY overrides NOTFOUND but not NONE. */
199 if (*site_level < TLS_LEV_NONE)
200 *site_level = TLS_LEV_MAY;
201 } else if (!strcasecmp(lookup, "MUST_NOPEERMATCH")) {
202 if (*site_level < TLS_LEV_ENCRYPT)
203 *site_level = TLS_LEV_ENCRYPT;
204 } else if (!strcasecmp(lookup, "MUST")) {
205 if (*site_level < TLS_LEV_VERIFY)
206 *site_level = TLS_LEV_VERIFY;
207 } else {
208 msg_warn("%s: unknown TLS policy '%s' for %s %s",
209 tls_per_site->title, lookup, site_class, site_name);
210 MARK_INVALID(tls->why, site_level);
211 return;
212 }
213 } else if (tls_per_site->error) {
214 msg_warn("%s: %s \"%s\": per-site table lookup error",
215 tls_per_site->title, site_class, site_name);
216 dsb_simple(tls->why, "4.3.0", "Temporary lookup error");
217 *site_level = TLS_LEV_INVALID;
218 return;
219 }
220 return;
221 }
222
223 /* tls_policy_lookup_one - look up destination TLS policy */
224
tls_policy_lookup_one(SMTP_TLS_POLICY * tls,int * site_level,const char * site_name,const char * site_class)225 static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
226 const char *site_name,
227 const char *site_class)
228 {
229 const char *lookup;
230 char *policy;
231 char *saved_policy = 0;
232 char *tok;
233 char *name;
234 char *val;
235 static VSTRING *cbuf;
236 char *free_me = 0;
237
238 #undef FREE_RETURN
239 #define FREE_RETURN do { \
240 if (saved_policy) \
241 myfree(saved_policy); \
242 if (free_me) \
243 myfree(free_me); \
244 return; \
245 } while (0)
246
247 #define INVALID_RETURN(why, levelp) do { \
248 MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0)
249
250 #define WHERE \
251 STR(vstring_sprintf(cbuf, "%s, %s \"%s\"", \
252 tls_policy->title, site_class, site_name))
253
254 if (cbuf == 0)
255 cbuf = vstring_alloc(10);
256
257 if ((lookup = maps_find(tls_policy, site_name, 0)) == 0) {
258 if (tls_policy->error) {
259 msg_warn("%s: policy table lookup error", WHERE);
260 MARK_INVALID(tls->why, site_level);
261 }
262 return;
263 }
264 saved_policy = policy = mystrdup(lookup);
265
266 if ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) == 0) {
267 msg_warn("%s: invalid empty policy", WHERE);
268 INVALID_RETURN(tls->why, site_level);
269 }
270 *site_level = tls_level_lookup(tok);
271 if (*site_level == TLS_LEV_INVALID) {
272 /* tls_level_lookup() logs no warning. */
273 msg_warn("%s: invalid security level \"%s\"", WHERE, tok);
274 INVALID_RETURN(tls->why, site_level);
275 }
276
277 /*
278 * Warn about ignored attributes when TLS is disabled.
279 */
280 if (*site_level < TLS_LEV_MAY) {
281 while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0)
282 msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
283 WHERE, tok);
284 FREE_RETURN;
285 }
286
287 /*
288 * Errors in attributes may have security consequences, don't ignore
289 * errors that can degrade security.
290 *
291 * Caution: normalize whitespace, to neutralize line break etc. characters
292 * inside the value portion of { name = value }.
293 */
294 while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
295 const char *err;
296
297 #define EXTPAR_OPT (EXTPAR_FLAG_STRIP | EXTPAR_FLAG_NORMAL_WS)
298
299 if ((tok[0] == CHARS_BRACE[0]
300 && (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_OPT)) != 0)
301 || (err = split_nameval(tok, &name, &val)) != 0) {
302 msg_warn("%s: malformed attribute/value pair \"%s\": %s",
303 WHERE, tok, err);
304 INVALID_RETURN(tls->why, site_level);
305 }
306 /* Only one instance per policy. */
307 if (!strcasecmp(name, "ciphers")) {
308 if (*val == 0) {
309 msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
310 INVALID_RETURN(tls->why, site_level);
311 }
312 if (tls->grade) {
313 msg_warn("%s: attribute \"%s\" is specified multiple times",
314 WHERE, name);
315 INVALID_RETURN(tls->why, site_level);
316 }
317 tls->grade = mystrdup(val);
318 continue;
319 }
320 /* Only one instance per policy. */
321 if (!strcasecmp(name, "protocols")) {
322 if (tls->protocols) {
323 msg_warn("%s: attribute \"%s\" is specified multiple times",
324 WHERE, name);
325 INVALID_RETURN(tls->why, site_level);
326 }
327 tls->protocols = mystrdup(val);
328 continue;
329 }
330 /* Only one instance per policy. */
331 if (!strcasecmp(name, "servername")) {
332 if (tls->sni) {
333 msg_warn("%s: attribute \"%s\" is specified multiple times",
334 WHERE, name);
335 INVALID_RETURN(tls->why, site_level);
336 }
337 if (valid_hostname(val, DONT_GRIPE))
338 tls->sni = mystrdup(val);
339 else {
340 msg_warn("%s: \"%s=%s\" specifies an invalid hostname",
341 WHERE, name, val);
342 INVALID_RETURN(tls->why, site_level);
343 }
344 continue;
345 }
346 /* Multiple instances per policy. */
347 if (!strcasecmp(name, "match")) {
348 if (*val == 0) {
349 msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
350 INVALID_RETURN(tls->why, site_level);
351 }
352 switch (*site_level) {
353 default:
354 msg_warn("%s: attribute \"%s\" invalid at security level "
355 "\"%s\"", WHERE, name, policy_name(*site_level));
356 INVALID_RETURN(tls->why, site_level);
357 break;
358 case TLS_LEV_FPRINT:
359 if (tls->matchargv == 0)
360 tls->matchargv = argv_split(val, "|");
361 else
362 argv_split_append(tls->matchargv, val, "|");
363 break;
364 case TLS_LEV_VERIFY:
365 case TLS_LEV_SECURE:
366 if (tls->matchargv == 0)
367 tls->matchargv = argv_split(val, ":");
368 else
369 argv_split_append(tls->matchargv, val, ":");
370 break;
371 }
372 continue;
373 }
374 /* Only one instance per policy. */
375 if (!strcasecmp(name, "exclude")) {
376 if (tls->exclusions) {
377 msg_warn("%s: attribute \"%s\" is specified multiple times",
378 WHERE, name);
379 INVALID_RETURN(tls->why, site_level);
380 }
381 tls->exclusions = vstring_strcpy(vstring_alloc(10), val);
382 continue;
383 }
384 /* Multiple instances per policy. */
385 if (!strcasecmp(name, "tafile")) {
386 /* Only makes sense if we're using CA-based trust */
387 if (!TLS_MUST_PKIX(*site_level)) {
388 msg_warn("%s: attribute \"%s\" invalid at security level"
389 " \"%s\"", WHERE, name, policy_name(*site_level));
390 INVALID_RETURN(tls->why, site_level);
391 }
392 if (*val == 0) {
393 msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
394 INVALID_RETURN(tls->why, site_level);
395 }
396 if (!tls->dane)
397 tls->dane = tls_dane_alloc();
398 if (!tls_dane_load_trustfile(tls->dane, val)) {
399 INVALID_RETURN(tls->why, site_level);
400 }
401 continue;
402 }
403 /* Last one wins. */
404 if (!strcasecmp(name, "connection_reuse")) {
405 if (strcasecmp(val, "yes") == 0) {
406 tls->conn_reuse = 1;
407 } else if (strcasecmp(val, "no") == 0) {
408 tls->conn_reuse = 0;
409 } else {
410 msg_warn("%s: attribute \"%s\" has bad value: \"%s\"",
411 WHERE, name, val);
412 INVALID_RETURN(tls->why, site_level);
413 }
414 continue;
415 }
416 /* Last one wins. */
417 if (!strcasecmp(name, "enable_rpk")) {
418 /* Ultimately ignored at some security levels */
419 if (strcasecmp(val, "yes") == 0) {
420 tls->enable_rpk = 1;
421 } else if (strcasecmp(val, "no") == 0) {
422 tls->enable_rpk = 0;
423 } else {
424 msg_warn("%s: attribute \"%s\" has bad value: \"%s\"",
425 WHERE, name, val);
426 INVALID_RETURN(tls->why, site_level);
427 }
428 continue;
429 }
430 /* Only one instance per policy. */
431 if (!strcasecmp(name, EXT_POLICY_TTL)) {
432 char *end;
433 long lval;
434
435 if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET) {
436 msg_warn("%s: attribute \"%s\" is specified multiple times",
437 WHERE, name);
438 INVALID_RETURN(tls->why, site_level);
439 }
440 if (!alldig(val) || ((lval = sane_strtol(val, &end, 10)),
441 ((tls->ext_policy_ttl = lval) != lval))
442 || *end != 0) {
443 msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
444 WHERE, name, val);
445 INVALID_RETURN(tls->why, site_level);
446 }
447 continue;
448 }
449 /* Only one instance per policy. */
450 if (!strcasecmp(name, EXT_POLICY_TYPE)) {
451 if (tls->ext_policy_type) {
452 msg_warn("%s: attribute \"%s\" is specified multiple times",
453 WHERE, name);
454 INVALID_RETURN(tls->why, site_level);
455 }
456 if (!valid_tlsrpt_policy_type(val)) {
457 msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
458 WHERE, name, val);
459 INVALID_RETURN(tls->why, site_level);
460 }
461 tls->ext_policy_type = mystrdup(val);
462 continue;
463 }
464 /* Only one instance per policy. */
465 if (!strcasecmp(name, EXT_POLICY_DOMAIN)) {
466 if (tls->ext_policy_domain) {
467 msg_warn("%s: attribute \"%s\" is specified multiple times",
468 WHERE, name);
469 INVALID_RETURN(tls->why, site_level);
470 }
471 if (!valid_hostname(val, DO_GRIPE)) {
472 msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
473 WHERE, name, val);
474 INVALID_RETURN(tls->why, site_level);
475 }
476 tls->ext_policy_domain = mystrdup(val);
477 continue;
478 }
479 /* Multiple instances per policy are allowed. */
480 if (!strcasecmp(name, EXT_POLICY_STRING)) {
481 if (tls->ext_policy_strings == 0)
482 tls->ext_policy_strings = argv_alloc(1);
483 argv_add(tls->ext_policy_strings, val, (char *) 0);
484 continue;
485 }
486 /* Multiple instances per policy are allowed. */
487 if (!strcasecmp(name, EXT_MX_HOST_PATTERN)) {
488 if (tls->ext_mx_host_patterns == 0)
489 tls->ext_mx_host_patterns = argv_alloc(1);
490 argv_add(tls->ext_mx_host_patterns, val, (char *) 0);
491 continue;
492 }
493 /* Only one instance per policy. */
494 if (!strcasecmp(name, EXT_POLICY_FAILURE)) {
495 if (tls->ext_policy_failure != 0) {
496 msg_warn("%s: attribute \"%s\" is specified multiple times",
497 WHERE, name);
498 INVALID_RETURN(tls->why, site_level);
499 }
500 if (!valid_tlsrpt_policy_failure(val)) {
501 msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
502 WHERE, name, val);
503 INVALID_RETURN(tls->why, site_level);
504 }
505 tls->ext_policy_failure = mystrdup(val);
506 continue;
507 }
508 msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
509 INVALID_RETURN(tls->why, site_level);
510 }
511 if (tls->ext_policy_type == 0) {
512 if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET
513 || tls->ext_policy_strings
514 || tls->ext_policy_domain || tls->ext_mx_host_patterns
515 || tls->ext_policy_failure) {
516 msg_warn("%s: built-in policy has unexpected attribute "
517 "policy_ttl, policy_domain, policy_string, "
518 "mx_host_pattern or policy_failure", WHERE);
519 INVALID_RETURN(tls->why, site_level);
520 }
521 }
522 FREE_RETURN;
523 }
524
525 /* tls_policy_lookup - look up destination TLS policy */
526
tls_policy_lookup(SMTP_TLS_POLICY * tls,int * site_level,const char * site_name,const char * site_class)527 static void tls_policy_lookup(SMTP_TLS_POLICY *tls, int *site_level,
528 const char *site_name,
529 const char *site_class)
530 {
531
532 /*
533 * Only one lookup with [nexthop]:port, [nexthop] or nexthop:port These
534 * are never the domain part of localpart@domain, rather they are
535 * explicit nexthops from transport:nexthop, and match only the
536 * corresponding policy. Parent domain matching (below) applies only to
537 * sub-domains of the recipient domain.
538 *
539 * XXX UNIX-domain connections query with the pathname as destination.
540 */
541 if (!valid_utf8_hostname(var_smtputf8_enable, site_name, DONT_GRIPE)) {
542 tls_policy_lookup_one(tls, site_level, site_name, site_class);
543 return;
544 }
545 do {
546 tls_policy_lookup_one(tls, site_level, site_name, site_class);
547 } while (*site_level == TLS_LEV_NOTFOUND
548 && (site_name = strchr(site_name + 1, '.')) != 0);
549 }
550
551 /* load_tas - load one or more ta files */
552
load_tas(TLS_DANE * dane,const char * files)553 static int load_tas(TLS_DANE *dane, const char *files)
554 {
555 int ret = 0;
556 char *save = mystrdup(files);
557 char *buf = save;
558 char *file;
559
560 do {
561 if ((file = mystrtok(&buf, CHARS_COMMA_SP)) != 0)
562 ret = tls_dane_load_trustfile(dane, file);
563 } while (file && ret);
564
565 myfree(save);
566 return (ret);
567 }
568
569 /* set_cipher_grade - Set cipher grade and exclusions */
570
set_cipher_grade(SMTP_TLS_POLICY * tls)571 static void set_cipher_grade(SMTP_TLS_POLICY *tls)
572 {
573 const char *mand_exclude = "";
574 const char *also_exclude = "";
575
576 /*
577 * Use main.cf cipher level if no per-destination value specified. With
578 * mandatory encryption at least encrypt, and with mandatory verification
579 * at least authenticate!
580 */
581 switch (tls->level) {
582 case TLS_LEV_INVALID:
583 case TLS_LEV_NONE:
584 return;
585
586 case TLS_LEV_MAY:
587 if (tls->grade == 0)
588 tls->grade = mystrdup(var_smtp_tls_ciph);
589 break;
590
591 case TLS_LEV_ENCRYPT:
592 if (tls->grade == 0)
593 tls->grade = mystrdup(var_smtp_tls_mand_ciph);
594 mand_exclude = var_smtp_tls_mand_excl;
595 also_exclude = "eNULL";
596 break;
597
598 case TLS_LEV_HALF_DANE:
599 case TLS_LEV_DANE:
600 case TLS_LEV_DANE_ONLY:
601 case TLS_LEV_FPRINT:
602 case TLS_LEV_VERIFY:
603 case TLS_LEV_SECURE:
604 if (tls->grade == 0)
605 tls->grade = mystrdup(var_smtp_tls_mand_ciph);
606 mand_exclude = var_smtp_tls_mand_excl;
607 also_exclude = "aNULL";
608 break;
609 }
610
611 #define ADD_EXCLUDE(vstr, str) \
612 do { \
613 if (*(str)) \
614 vstring_sprintf_append((vstr), "%s%s", \
615 VSTRING_LEN(vstr) ? " " : "", (str)); \
616 } while (0)
617
618 /*
619 * The "exclude" policy table attribute overrides main.cf exclusion
620 * lists.
621 */
622 if (tls->exclusions == 0) {
623 tls->exclusions = vstring_alloc(10);
624 ADD_EXCLUDE(tls->exclusions, var_smtp_tls_excl_ciph);
625 ADD_EXCLUDE(tls->exclusions, mand_exclude);
626 }
627 ADD_EXCLUDE(tls->exclusions, also_exclude);
628 }
629
630 /* policy_create - create SMTP TLS policy cache object (ctable call-back) */
631
policy_create(const char * unused_key,void * context)632 static void *policy_create(const char *unused_key, void *context)
633 {
634 SMTP_ITERATOR *iter = (SMTP_ITERATOR *) context;
635 int site_level;
636 const char *dest = STR(iter->dest);
637 const char *host = STR(iter->host);
638
639 /*
640 * Prepare a pristine policy object.
641 */
642 SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *) mymalloc(sizeof(*tls));
643
644 smtp_tls_policy_init(tls, dsb_create());
645 tls->conn_reuse = var_smtp_tls_conn_reuse;
646 tls->enable_rpk = var_smtp_tls_enable_rpk;
647
648 /*
649 * Compute the per-site TLS enforcement level. For compatibility with the
650 * original TLS patch, this algorithm is gives equal precedence to host
651 * and next-hop policies.
652 */
653 tls->level = global_tls_level();
654 site_level = TLS_LEV_NOTFOUND;
655
656 if (tls_policy) {
657 tls_policy_lookup(tls, &site_level, dest, "next-hop destination");
658 } else if (tls_per_site) {
659 tls_site_lookup(tls, &site_level, dest, "next-hop destination");
660 if (site_level != TLS_LEV_INVALID
661 && strcasecmp_utf8(dest, host) != 0)
662 tls_site_lookup(tls, &site_level, host, "server hostname");
663
664 /*
665 * Override a wild-card per-site policy with a more specific global
666 * policy.
667 *
668 * With the original TLS patch, 1) a per-site ENCRYPT could not override
669 * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
670 * produced inconsistent results: it changed a global VERIFY into
671 * NONE, while producing MAY with all weaker global policy settings.
672 *
673 * With the current implementation, a combined per-site (NONE+MAY)
674 * consistently overrides global policy with NONE, and global policy
675 * can override only a per-site MAY wildcard. That is, specific
676 * policies consistently override wildcard policies, and
677 * (non-wildcard) per-site policies consistently override global
678 * policies.
679 */
680 if (site_level == TLS_LEV_MAY && tls->level > TLS_LEV_MAY)
681 site_level = tls->level;
682 }
683 switch (site_level) {
684 default:
685 tls->level = site_level;
686 /* FALLTHROUGH */
687 case TLS_LEV_NOTFOUND:
688 break;
689 case TLS_LEV_INVALID:
690 tls->level = site_level;
691 return ((void *) tls);
692 }
693
694 /*
695 * DANE initialization may change the security level to something else,
696 * so do this early, so that we use the right level below. Note that
697 * "dane-only" changes to "dane" once we obtain the requisite TLSA
698 * records.
699 */
700 if (TLS_DANE_BASED(tls->level))
701 dane_init(tls, iter);
702 if (tls->level == TLS_LEV_INVALID)
703 return ((void *) tls);
704
705 /*
706 * Use main.cf protocols and SNI settings if not set in per-destination
707 * table.
708 */
709 if (tls->level > TLS_LEV_NONE && tls->protocols == 0)
710 tls->protocols =
711 mystrdup((tls->level == TLS_LEV_MAY) ?
712 var_smtp_tls_proto : var_smtp_tls_mand_proto);
713 if (tls->level > TLS_LEV_NONE && tls->sni == 0) {
714 if (!*var_smtp_tls_sni || valid_hostname(var_smtp_tls_sni, DONT_GRIPE))
715 tls->sni = mystrdup(var_smtp_tls_sni);
716 else {
717 msg_warn("\"%s = %s\" specifies an invalid hostname",
718 VAR_LMTP_SMTP(TLS_SNI), var_smtp_tls_sni);
719 MARK_INVALID(tls->why, &tls->level);
720 return ((void *) tls);
721 }
722 }
723
724 /*
725 * Compute cipher grade (if set in per-destination table, else
726 * set_cipher() uses main.cf settings) and security level dependent
727 * cipher exclusion list.
728 */
729 set_cipher_grade(tls);
730
731 /*
732 * Even when soliciting raw public keys, synthesize TLSA RRs that also match
733 * certificates. Though this is fragile, it maintains compatibility with
734 * servers that never return RPKs.
735 */
736 #define DONT_SUPPRESS_CERT_MATCH 0
737
738 /*
739 * Use main.cf cert_match setting if not set in per-destination table.
740 */
741 switch (tls->level) {
742 case TLS_LEV_INVALID:
743 case TLS_LEV_NONE:
744 case TLS_LEV_MAY:
745 case TLS_LEV_ENCRYPT:
746 case TLS_LEV_HALF_DANE:
747 case TLS_LEV_DANE:
748 case TLS_LEV_DANE_ONLY:
749 break;
750 case TLS_LEV_FPRINT:
751 if (tls->dane == 0)
752 tls->dane = tls_dane_alloc();
753 /* Process the specified fingerprint match patterns */
754 if (tls->matchargv) {
755 int i;
756
757 for (i = 0; i < tls->matchargv->argc; ++i) {
758 tls_dane_add_fpt_digests(tls->dane, DONT_SUPPRESS_CERT_MATCH,
759 tls->matchargv->argv[i], "",
760 smtp_mode);
761 }
762 } else {
763 tls_dane_add_fpt_digests(tls->dane, DONT_SUPPRESS_CERT_MATCH,
764 var_smtp_tls_fpt_cmatch, CHARS_COMMA_SP,
765 smtp_mode);
766 }
767 if (tls->dane->tlsa == 0) {
768 msg_warn("nexthop domain %s: configured at fingerprint "
769 "security level, but with no fingerprints to match.",
770 dest);
771 MARK_INVALID(tls->why, &tls->level);
772 return ((void *) tls);
773 }
774 break;
775 case TLS_LEV_VERIFY:
776 case TLS_LEV_SECURE:
777 if (tls->matchargv == 0)
778 tls->matchargv =
779 argv_split(tls->level == TLS_LEV_VERIFY ?
780 var_smtp_tls_vfy_cmatch : var_smtp_tls_sec_cmatch,
781 CHARS_COMMA_SP ":");
782 if (*var_smtp_tls_tafile) {
783 if (tls->dane == 0)
784 tls->dane = tls_dane_alloc();
785 if (tls->dane->tlsa == 0
786 && !load_tas(tls->dane, var_smtp_tls_tafile)) {
787 MARK_INVALID(tls->why, &tls->level);
788 return ((void *) tls);
789 }
790 }
791 break;
792 default:
793 msg_panic("unexpected TLS security level: %d", tls->level);
794 }
795
796 if (msg_verbose && tls->level != global_tls_level())
797 msg_info("%s TLS level: %s", "effective", policy_name(tls->level));
798
799 return ((void *) tls);
800 }
801
802 /* policy_delete - free no longer cached policy (ctable call-back) */
803
policy_delete(void * item,void * unused_context)804 static void policy_delete(void *item, void *unused_context)
805 {
806 SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *) item;
807
808 if (tls->protocols)
809 myfree(tls->protocols);
810 if (tls->sni)
811 myfree(tls->sni);
812 if (tls->grade)
813 myfree(tls->grade);
814 if (tls->exclusions)
815 vstring_free(tls->exclusions);
816 if (tls->matchargv)
817 argv_free(tls->matchargv);
818 if (tls->dane)
819 tls_dane_free(tls->dane);
820 dsb_free(tls->why);
821 if (tls->ext_policy_type)
822 myfree(tls->ext_policy_type);
823 if (tls->ext_policy_domain)
824 myfree(tls->ext_policy_domain);
825 if (tls->ext_policy_strings)
826 argv_free(tls->ext_policy_strings);
827 if (tls->ext_mx_host_patterns)
828 argv_free(tls->ext_mx_host_patterns);
829 if (tls->ext_policy_failure)
830 myfree(tls->ext_policy_failure);
831
832 myfree((void *) tls);
833 }
834
835 /* smtp_tls_policy_cache_query - cached lookup of TLS policy */
836
smtp_tls_policy_cache_query(DSN_BUF * why,SMTP_TLS_POLICY * tls,SMTP_ITERATOR * iter)837 int smtp_tls_policy_cache_query(DSN_BUF *why, SMTP_TLS_POLICY *tls,
838 SMTP_ITERATOR *iter)
839 {
840 VSTRING *key;
841
842 /*
843 * Create an empty TLS Policy cache on the fly.
844 */
845 if (policy_cache == 0)
846 policy_cache =
847 ctable_create(CACHE_SIZE, policy_create, policy_delete, (void *) 0);
848
849 /*
850 * Query the TLS Policy cache, with a search key that reflects our shared
851 * values that also appear in other cache and table search keys.
852 */
853 key = vstring_alloc(100);
854 smtp_key_prefix(key, ":", iter, SMTP_KEY_FLAG_CUR_NEXTHOP
855 | SMTP_KEY_FLAG_HOSTNAME
856 | SMTP_KEY_FLAG_PORT);
857 ctable_newcontext(policy_cache, (void *) iter);
858 *tls = *(SMTP_TLS_POLICY *) ctable_locate(policy_cache, STR(key));
859 vstring_free(key);
860
861 /*
862 * Report errors. Both error and non-error results are cached. We must
863 * therefore copy the cached DSN buffer content to the caller's buffer.
864 */
865 if (tls->level == TLS_LEV_INVALID) {
866 /* XXX Simplify this by implementing a "copy" primitive. */
867 dsb_update(why,
868 STR(tls->why->status), STR(tls->why->action),
869 STR(tls->why->mtype), STR(tls->why->mname),
870 STR(tls->why->dtype), STR(tls->why->dtext),
871 "%s", STR(tls->why->reason));
872 return (0);
873 } else {
874 return (1);
875 }
876 }
877
878 /* smtp_tls_policy_cache_flush - flush TLS policy cache */
879
smtp_tls_policy_cache_flush(void)880 void smtp_tls_policy_cache_flush(void)
881 {
882 if (policy_cache != 0) {
883 ctable_free(policy_cache);
884 policy_cache = 0;
885 }
886 }
887
888 /* global_tls_level - parse and cache var_smtp_tls_level */
889
global_tls_level(void)890 static int global_tls_level(void)
891 {
892 static int l = TLS_LEV_NOTFOUND;
893
894 if (l != TLS_LEV_NOTFOUND)
895 return l;
896
897 /*
898 * Compute the global TLS policy. This is the default policy level when
899 * no per-site policy exists. It also is used to override a wild-card
900 * per-site policy.
901 *
902 * We require that the global level is valid on startup.
903 */
904 if (*var_smtp_tls_level) {
905 if ((l = tls_level_lookup(var_smtp_tls_level)) == TLS_LEV_INVALID)
906 msg_fatal("invalid tls security level: \"%s\"", var_smtp_tls_level);
907 } else if (var_smtp_enforce_tls)
908 l = var_smtp_tls_enforce_peername ? TLS_LEV_VERIFY : TLS_LEV_ENCRYPT;
909 else
910 l = var_smtp_use_tls ? TLS_LEV_MAY : TLS_LEV_NONE;
911
912 if (msg_verbose)
913 msg_info("%s TLS level: %s", "global", policy_name(l));
914
915 return l;
916 }
917
918 #define NONDANE_CONFIG 0 /* Administrator's fault */
919 #define NONDANE_DEST 1 /* Remote server's fault */
920 #define DANE_CANTAUTH 2 /* Remote server's fault */
921
dane_incompat(SMTP_TLS_POLICY * tls,SMTP_ITERATOR * iter,int errtype,const char * fmt,...)922 static void PRINTFLIKE(4, 5) dane_incompat(SMTP_TLS_POLICY *tls,
923 SMTP_ITERATOR *iter,
924 int errtype,
925 const char *fmt,...)
926 {
927 va_list ap;
928
929 va_start(ap, fmt);
930 if (tls->level == TLS_LEV_DANE) {
931 tls->level = (errtype == DANE_CANTAUTH) ? TLS_LEV_ENCRYPT : TLS_LEV_MAY;
932 if (errtype == NONDANE_CONFIG)
933 vmsg_warn(fmt, ap);
934 else if (msg_verbose)
935 vmsg_info(fmt, ap);
936 } else { /* dane-only */
937 if (errtype == NONDANE_CONFIG) {
938 vmsg_warn(fmt, ap);
939 MARK_INVALID(tls->why, &tls->level);
940 } else {
941 tls->level = TLS_LEV_INVALID;
942 vdsb_simple(tls->why, "4.7.5", fmt, ap);
943 }
944 }
945 va_end(ap);
946 }
947
948 /* dane_init - special initialization for "dane" security level */
949
dane_init(SMTP_TLS_POLICY * tls,SMTP_ITERATOR * iter)950 static void dane_init(SMTP_TLS_POLICY *tls, SMTP_ITERATOR *iter)
951 {
952 TLS_DANE *dane;
953
954 if (!iter->port) {
955 msg_warn("%s: the \"dane\" security level is invalid for delivery via"
956 " unix-domain sockets", STR(iter->dest));
957 MARK_INVALID(tls->why, &tls->level);
958 return;
959 }
960 if (!tls_dane_avail()) {
961 dane_incompat(tls, iter, NONDANE_CONFIG,
962 "%s: %s configured, but no requisite library support",
963 STR(iter->dest), policy_name(tls->level));
964 return;
965 }
966 if (!(smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS)
967 || smtp_dns_support != SMTP_DNS_DNSSEC) {
968 dane_incompat(tls, iter, NONDANE_CONFIG,
969 "%s: %s configured with dnssec lookups disabled",
970 STR(iter->dest), policy_name(tls->level));
971 return;
972 }
973
974 /*
975 * If we ignore MX lookup errors, we also ignore DNSSEC security problems
976 * and thus avoid any reasonable expectation that we get the right DANE
977 * key material.
978 */
979 if (smtp_mode && var_ign_mx_lookup_err) {
980 dane_incompat(tls, iter, NONDANE_CONFIG,
981 "%s: %s configured with MX lookup errors ignored",
982 STR(iter->dest), policy_name(tls->level));
983 return;
984 }
985
986 /*
987 * This is not optional, code in tls_dane.c assumes that the nexthop
988 * qname is already an fqdn. If we're using these flags to go from qname
989 * to rname, the assumption is invalid. Likewise we cannot add the qname
990 * to certificate name checks, ...
991 */
992 if (smtp_dns_res_opt & (RES_DEFNAMES | RES_DNSRCH)) {
993 dane_incompat(tls, iter, NONDANE_CONFIG,
994 "%s: dns resolver options incompatible with %s TLS",
995 STR(iter->dest), policy_name(tls->level));
996 return;
997 }
998
999 /*
1000 * When the MX name is present and insecure, DANE may not apply, we then
1001 * either fail if DANE is mandatory or use regular opportunistic TLS if
1002 * the insecure MX level is "may".
1003 */
1004 if (iter->mx && !iter->mx->dnssec_valid
1005 && (tls->level == TLS_LEV_DANE_ONLY ||
1006 smtp_tls_insecure_mx_policy <= TLS_LEV_MAY)) {
1007 dane_incompat(tls, iter, NONDANE_DEST, "non DNSSEC destination");
1008 return;
1009 }
1010 /* When TLSA lookups fail, we defer the message */
1011 if ((dane = tls_dane_resolve(iter->port, "tcp", iter->rr,
1012 var_smtp_tls_force_tlsa)) == 0) {
1013 tls->level = TLS_LEV_INVALID;
1014 dsb_simple(tls->why, "4.7.5", "TLSA lookup error for %s:%u",
1015 STR(iter->host), ntohs(iter->port));
1016 return;
1017 }
1018 if (tls_dane_notfound(dane)) {
1019 dane_incompat(tls, iter, NONDANE_DEST, "no TLSA records found");
1020 tls_dane_free(dane);
1021 return;
1022 }
1023
1024 /*
1025 * Some TLSA records found, but none usable, per
1026 *
1027 * https://tools.ietf.org/html/draft-ietf-dane-srv-02#section-4
1028 *
1029 * we MUST use TLS, and SHALL use full PKIX certificate checks. The latter
1030 * would be unwise for SMTP: no human present to "click ok" and risk of
1031 * non-delivery in most cases exceeds risk of interception.
1032 *
1033 * We also have a form of Goedel's incompleteness theorem in play: any list
1034 * of public root CA certs is either incomplete or inconsistent (for any
1035 * given verifier some of the CAs are surely not trustworthy).
1036 */
1037 if (tls_dane_unusable(dane)) {
1038 dane_incompat(tls, iter, DANE_CANTAUTH, "TLSA records unusable");
1039 tls_dane_free(dane);
1040 return;
1041 }
1042
1043 /*
1044 * Perhaps downgrade to "encrypt" if MX is insecure.
1045 */
1046 if (iter->mx && !iter->mx->dnssec_valid) {
1047 if (smtp_tls_insecure_mx_policy == TLS_LEV_ENCRYPT) {
1048 dane_incompat(tls, iter, DANE_CANTAUTH,
1049 "Verification not possible, MX RRset is insecure");
1050 tls_dane_free(dane);
1051 return;
1052 }
1053 if (tls->level != TLS_LEV_DANE
1054 || smtp_tls_insecure_mx_policy != TLS_LEV_DANE)
1055 msg_panic("wrong state for insecure MX host DANE policy");
1056
1057 /* For correct logging in tls_client_start() */
1058 tls->level = TLS_LEV_HALF_DANE;
1059 }
1060
1061 /*
1062 * With DANE trust anchors, peername matching is not configurable.
1063 */
1064 if (dane->tlsa != 0) {
1065 tls->matchargv = argv_alloc(2);
1066 argv_add(tls->matchargv, dane->base_domain, ARGV_END);
1067 if (iter->mx) {
1068 if (strcmp(iter->mx->qname, iter->mx->rname) == 0)
1069 argv_add(tls->matchargv, iter->mx->qname, ARGV_END);
1070 else
1071 argv_add(tls->matchargv, iter->mx->rname,
1072 iter->mx->qname, ARGV_END);
1073 }
1074 } else
1075 msg_panic("empty DANE match list");
1076 tls->dane = dane;
1077 return;
1078 }
1079
1080 #endif
1081