static void session_tls_init(SMTP_SESSION *session, const char *dest, const char *host, int flags) { const char *myname = "session_tls_init"; int global_level; int site_level; /* * Initialize all TLS related session properties. */ session->tls_context = 0; session->tls_nexthop = 0; session->tls_level = TLS_LEV_NONE; session->tls_retry_plain = 0; session->tls_protocols = 0; session->tls_grade = 0; session->tls_exclusions = 0; session->tls_matchargv = 0; /* * Compute the global TLS policy. This is the default policy level when * no per-site policy exists. It also is used to override a wild-card * per-site policy. */ if (*var_smtp_tls_level) { /* Require that var_smtp_tls_level is sanitized upon startup. */ global_level = tls_level_lookup(var_smtp_tls_level); if (global_level == TLS_LEV_INVALID) msg_panic("%s: invalid TLS security level: \"%s\"", myname, var_smtp_tls_level); } else if (var_smtp_enforce_tls) { global_level = var_smtp_tls_enforce_peername ? TLS_LEV_VERIFY : TLS_LEV_ENCRYPT; } else { global_level = var_smtp_use_tls ? TLS_LEV_MAY : TLS_LEV_NONE; } if (msg_verbose) msg_info("%s TLS level: %s", "global", policy_name(global_level)); /* * Compute the per-site TLS enforcement level. For compatibility with the * original TLS patch, this algorithm is gives equal precedence to host * and next-hop policies. */ site_level = TLS_LEV_NOTFOUND; if (tls_policy) { tls_policy_lookup(session, &site_level, dest, "next-hop destination"); } else if (tls_per_site) { tls_site_lookup(&site_level, dest, "next-hop destination"); if (strcasecmp(dest, host) != 0) tls_site_lookup(&site_level, host, "server hostname"); if (msg_verbose) msg_info("%s TLS level: %s", "site", policy_name(site_level)); /* * Override a wild-card per-site policy with a more specific global * policy. * * With the original TLS patch, 1) a per-site ENCRYPT could not override * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy * produced inconsistent results: it changed a global VERIFY into * NONE, while producing MAY with all weaker global policy settings. * * With the current implementation, a combined per-site (NONE+MAY) * consistently overrides global policy with NONE, and global policy * can override only a per-site MAY wildcard. That is, specific * policies consistently override wildcard policies, and * (non-wildcard) per-site policies consistently override global * policies. */ if (site_level == TLS_LEV_MAY && global_level > TLS_LEV_MAY) site_level = global_level; } if (site_level == TLS_LEV_NOTFOUND) session->tls_level = global_level; else session->tls_level = site_level; /* * Use main.cf protocols setting if not set in per-destination table. */ if (session->tls_level > TLS_LEV_NONE && session->tls_protocols == 0) session->tls_protocols = mystrdup((session->tls_level == TLS_LEV_MAY) ? var_smtp_tls_proto : var_smtp_tls_mand_proto); /* * Compute cipher grade (if set in per-destination table, else * set_cipher() uses main.cf settings) and security level dependent * cipher exclusion list. */ set_cipher_grade(session); /* * Use main.cf cert_match setting if not set in per-destination table. */ if (session->tls_matchargv == 0) { switch (session->tls_level) { case TLS_LEV_INVALID: case TLS_LEV_NONE: case TLS_LEV_MAY: case TLS_LEV_ENCRYPT: break; case TLS_LEV_FPRINT: session->tls_matchargv = argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |"); break; case TLS_LEV_VERIFY: session->tls_matchargv = argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :"); break; case TLS_LEV_SECURE: session->tls_matchargv = argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :"); break; default: msg_panic("unexpected TLS security level: %d", session->tls_level); } } if (msg_verbose && (tls_policy || tls_per_site)) msg_info("%s TLS level: %s", "effective", policy_name(session->tls_level)); }
static void *policy_create(const char *unused_key, void *context) { SMTP_ITERATOR *iter = (SMTP_ITERATOR *) context; int site_level; const char *dest = STR(iter->dest); const char *host = STR(iter->host); /* * Prepare a pristine policy object. */ SMTP_TLS_POLICY *tls = (SMTP_TLS_POLICY *) mymalloc(sizeof(*tls)); smtp_tls_policy_init(tls, dsb_create()); /* * Compute the per-site TLS enforcement level. For compatibility with the * original TLS patch, this algorithm is gives equal precedence to host * and next-hop policies. */ tls->level = global_tls_level(); site_level = TLS_LEV_NOTFOUND; if (tls_policy) { tls_policy_lookup(tls, &site_level, dest, "next-hop destination"); } else if (tls_per_site) { tls_site_lookup(tls, &site_level, dest, "next-hop destination"); if (site_level != TLS_LEV_INVALID && strcasecmp(dest, host) != 0) tls_site_lookup(tls, &site_level, host, "server hostname"); /* * Override a wild-card per-site policy with a more specific global * policy. * * With the original TLS patch, 1) a per-site ENCRYPT could not override * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy * produced inconsistent results: it changed a global VERIFY into * NONE, while producing MAY with all weaker global policy settings. * * With the current implementation, a combined per-site (NONE+MAY) * consistently overrides global policy with NONE, and global policy * can override only a per-site MAY wildcard. That is, specific * policies consistently override wildcard policies, and * (non-wildcard) per-site policies consistently override global * policies. */ if (site_level == TLS_LEV_MAY && tls->level > TLS_LEV_MAY) site_level = tls->level; } switch (site_level) { default: tls->level = site_level; case TLS_LEV_NOTFOUND: break; case TLS_LEV_INVALID: return ((void *) tls); } /* * DANE initialization may change the security level to something else, * so do this early, so that we use the right level below. Note that * "dane-only" changes to "dane" once we obtain the requisite TLSA * records. */ if (tls->level == TLS_LEV_DANE || tls->level == TLS_LEV_DANE_ONLY) dane_init(tls, iter); if (tls->level == TLS_LEV_INVALID) return ((void *) tls); /* * Use main.cf protocols setting if not set in per-destination table. */ if (tls->level > TLS_LEV_NONE && tls->protocols == 0) tls->protocols = mystrdup((tls->level == TLS_LEV_MAY) ? var_smtp_tls_proto : var_smtp_tls_mand_proto); /* * Compute cipher grade (if set in per-destination table, else * set_cipher() uses main.cf settings) and security level dependent * cipher exclusion list. */ set_cipher_grade(tls); /* * Use main.cf cert_match setting if not set in per-destination table. */ switch (tls->level) { case TLS_LEV_INVALID: case TLS_LEV_NONE: case TLS_LEV_MAY: case TLS_LEV_ENCRYPT: case TLS_LEV_DANE: break; case TLS_LEV_FPRINT: if (tls->dane == 0) tls->dane = tls_dane_alloc(); if (!TLS_DANE_HASEE(tls->dane)) { tls_dane_add_ee_digests(tls->dane, var_smtp_tls_fpt_dgst, var_smtp_tls_fpt_cmatch, "\t\n\r, "); if (!TLS_DANE_HASEE(tls->dane)) { msg_warn("nexthop domain %s: configured at fingerprint " "security level, but with no fingerprints to match.", dest); MARK_INVALID(tls->why, &tls->level); return ((void *) tls); } } break; case TLS_LEV_VERIFY: case TLS_LEV_SECURE: if (tls->matchargv == 0) tls->matchargv = argv_split(tls->level == TLS_LEV_VERIFY ? var_smtp_tls_vfy_cmatch : var_smtp_tls_sec_cmatch, "\t\n\r, :"); if (*var_smtp_tls_tafile) { if (tls->dane == 0) tls->dane = tls_dane_alloc(); if (!TLS_DANE_HASTA(tls->dane) && !load_tas(tls->dane, var_smtp_tls_tafile)) { MARK_INVALID(tls->why, &tls->level); return ((void *) tls); } } break; default: msg_panic("unexpected TLS security level: %d", tls->level); } if (msg_verbose && tls->level != global_tls_level()) msg_info("%s TLS level: %s", "effective", policy_name(tls->level)); return ((void *) tls); }