示例#1
0
static VSTRING *flush_site_to_path(VSTRING *path, const char *site)
{
    const char *ptr;
    int     ch;

    /*
     * Convert the name to ASCII, so that we don't to end up with non-ASCII
     * names in the file system. The IDNA library functions fold case.
     */
#ifndef NO_EAI
    if ((site = midna_domain_to_ascii(site)) == 0)
	return (0);
#endif

    /*
     * Allocate buffer on the fly; caller still needs to clean up.
     */
    if (path == 0)
	path = vstring_alloc(10);

    /*
     * Mask characters that could upset the name-to-queue-file mapping code.
     */
    for (ptr = site; (ch = *(unsigned const char *) ptr) != 0; ptr++)
	if (ISALNUM(ch))
	    VSTRING_ADDCH(path, tolower(ch));
	else
	    VSTRING_ADDCH(path, '_');
    VSTRING_TERMINATE(path);

    if (msg_verbose)
	msg_info("site %s to path %s", site, STR(path));

    return (path);
}
示例#2
0
DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
{
    DNS_RR *addr_list;
    int     res_opt = 0;
    const char *ahost;

    dsb_reset(why);				/* Paranoia */

    if (smtp_dns_support == SMTP_DNS_DNSSEC)
	res_opt |= RES_USE_DNSSEC;

    /*
     * IDNA support.
     */
#ifndef NO_EAI
    if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) {
	if (msg_verbose)
	    msg_info("%s asciified to %s", host, ahost);
    } else
#endif
	ahost = host;

    /*
     * If the host is specified by numerical address, just convert the
     * address to internal form. Otherwise, the host is specified by name.
     */
#define PREF0	0
    addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, why);
    if (addr_list
	&& (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
	&& smtp_find_self(addr_list) != 0) {
	dns_rr_free(addr_list);
	dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host);
	return (0);
    }
    if (addr_list && addr_list->next) {
	if (var_smtp_rand_addr)
	    addr_list = dns_rr_shuffle(addr_list);
	/* The following changes the order of equal-preference hosts. */
	if (inet_proto_info()->ai_family_list[1] != 0)
	    addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
    }
    if (msg_verbose)
	smtp_print_addr(host, addr_list);
    return (addr_list);
}
示例#3
0
int     valid_utf8_hostname(int enable_utf8, const char *name, int gripe)
{
    static const char myname[] = "valid_utf8_hostname";

    /*
     * Trivial cases first.
     */
    if (*name == 0) {
	if (gripe)
	    msg_warn("%s: empty domain name", myname);
	return (0);
    }

    /*
     * Convert non-ASCII domain name to ASCII and validate the result per
     * STD3. midna_domain_to_ascii() applies valid_hostname() to the result.
     * Propagate the gripe parameter for better diagnostics (note that
     * midna_domain_to_ascii() logs a problem only when the result is not
     * cached).
     */
#ifndef NO_EAI
    if (enable_utf8 && !allascii(name)) {
	if (midna_domain_to_ascii(name) == 0) {
	    if (gripe)
		msg_warn("%s: malformed UTF-8 domain name", myname);
	    return (0);
	} else {
	    return (1);
	}
    }
#endif

    /*
     * Validate ASCII name per STD3.
     */
    return (valid_hostname(name, gripe));
}
示例#4
0
static int match_servername(const char *certid,
			            const TLS_CLIENT_START_PROPS *props)
{
    const ARGV *cmatch_argv;
    const char *nexthop = props->nexthop;
    const char *hname = props->host;
    const char *domain;
    const char *parent;
    const char *aname;
    int     match_subdomain;
    int     i;
    int     idlen;
    int     domlen;

    if ((cmatch_argv = props->matchargv) == 0)
	return 0;

#ifndef NO_EAI

    /*
     * DNS subjectAltNames are required to be ASCII.
     * 
     * Per RFC 6125 Section 6.4.4 Matching the CN-ID, follows the same rules
     * (6.4.1, 6.4.2 and 6.4.3) that apply to subjectAltNames.  In
     * particular, 6.4.2 says that the reference identifier is coerced to
     * ASCII, but no conversion is stated or implied for the CN-ID, so it
     * seems it only matches if it is all ASCII.  Otherwise, it is some other
     * sort of name.
     */
    if (!allascii(certid))
	return (0);
    if (!allascii(nexthop) && (aname = midna_domain_to_ascii(nexthop)) != 0) {
	if (msg_verbose)
	    msg_info("%s asciified to %s", nexthop, aname);
	nexthop = aname;
    }
#endif

    /*
     * Match the certid against each pattern until we find a match.
     */
    for (i = 0; i < cmatch_argv->argc; ++i) {
	match_subdomain = 0;
	if (!strcasecmp(cmatch_argv->argv[i], "nexthop"))
	    domain = nexthop;
	else if (!strcasecmp(cmatch_argv->argv[i], "hostname"))
	    domain = hname;
	else if (!strcasecmp(cmatch_argv->argv[i], "dot-nexthop")) {
	    domain = nexthop;
	    match_subdomain = 1;
	} else {
	    domain = cmatch_argv->argv[i];
	    if (*domain == '.') {
		if (domain[1]) {
		    ++domain;
		    match_subdomain = 1;
		}
	    }
#ifndef NO_EAI

	    /*
	     * Besides U+002E (full stop) IDNA2003 allows labels to be
	     * separated by any of the Unicode variants U+3002 (ideographic
	     * full stop), U+FF0E (fullwidth full stop), and U+FF61
	     * (halfwidth ideographic full stop). Their respective UTF-8
	     * encodings are: E38082, EFBC8E and EFBDA1.
	     * 
	     * IDNA2008 does not permit (upper) case and other variant
	     * differences in U-labels. The midna_domain_to_ascii() function,
	     * based on UTS46, normalizes such differences away.
	     * 
	     * The IDNA to_ASCII conversion does not allow empty leading labels,
	     * so we handle these explicitly here.
	     */
	    else {
		unsigned char *cp = (unsigned char *) domain;

		if ((cp[0] == 0xe3 && cp[1] == 0x80 && cp[2] == 0x82)
		    || (cp[0] == 0xef && cp[1] == 0xbc && cp[2] == 0x8e)
		    || (cp[0] == 0xef && cp[1] == 0xbd && cp[2] == 0xa1)) {
		    if (domain[3]) {
			domain = domain + 3;
			match_subdomain = 1;
		    }
		}
	    }
	    if (!allascii(domain)
		&& (aname = midna_domain_to_ascii(domain)) != 0) {
		if (msg_verbose)
		    msg_info("%s asciified to %s", domain, aname);
		domain = aname;
	    }
#endif
	}

	/*
	 * Sub-domain match: certid is any sub-domain of hostname.
	 */
	if (match_subdomain) {
	    if ((idlen = strlen(certid)) > (domlen = strlen(domain)) + 1
		&& certid[idlen - domlen - 1] == '.'
		&& !strcasecmp(certid + (idlen - domlen), domain))
		return (1);
	    else
		continue;
	}

	/*
	 * Exact match and initial "*" match. The initial "*" in a certid
	 * matches one (if var_tls_multi_label is false) or more hostname
	 * components under the condition that the certid contains multiple
	 * hostname components.
	 */
	if (!strcasecmp(certid, domain)
	    || (certid[0] == '*' && certid[1] == '.' && certid[2] != 0
		&& (parent = strchr(domain, '.')) != 0
		&& (idlen = strlen(certid + 1)) <= (domlen = strlen(parent))
		&& strcasecmp(var_tls_multi_wildcard == 0 ? parent :
			      parent + domlen - idlen,
			      certid + 1) == 0))
	    return (1);
    }
    return (0);
}
示例#5
0
DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags,
			         DSN_BUF *why, int *found_myself)
{
    DNS_RR *mx_names;
    DNS_RR *addr_list = 0;
    DNS_RR *self = 0;
    unsigned best_pref;
    unsigned best_found;
    int     r = 0;			/* Resolver flags */
    const char *aname;

    dsb_reset(why);				/* Paranoia */

    /*
     * Preferences from DNS use 0..32767, fall-backs use 32768+.
     */
#define IMPOSSIBLE_PREFERENCE	(~0)

    /*
     * Sanity check.
     */
    if (smtp_dns_support == SMTP_DNS_DISABLED)
	msg_panic("smtp_domain_addr: DNS lookup is disabled");
    if (smtp_dns_support == SMTP_DNS_DNSSEC)
	r |= RES_USE_DNSSEC;

    /*
     * IDNA support.
     */
#ifndef NO_EAI
    if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) {
	if (msg_verbose)
	    msg_info("%s asciified to %s", name, aname);
    } else
#endif
	aname = name;

    /*
     * Look up the mail exchanger hosts listed for this name. Sort the
     * results by preference. Look up the corresponding host addresses, and
     * truncate the list so that it contains only hosts that are more
     * preferred than myself. When no MX resource records exist, look up the
     * addresses listed for this name.
     * 
     * According to RFC 974: "It is possible that the list of MXs in the
     * response to the query will be empty.  This is a special case.  If the
     * list is empty, mailers should treat it as if it contained one RR, an
     * MX RR with a preference value of 0, and a host name of REMOTE.  (I.e.,
     * REMOTE is its only MX).  In addition, the mailer should do no further
     * processing on the list, but should attempt to deliver the message to
     * REMOTE."
     * 
     * Normally it is OK if an MX host cannot be found in the DNS; we'll just
     * use a backup one, and silently ignore the better MX host. However, if
     * the best backup that we can find in the DNS is the local machine, then
     * we must remember that the local machine is not the primary MX host, or
     * else we will claim that mail loops back.
     * 
     * XXX Optionally do A lookups even when the MX lookup didn't complete.
     * Unfortunately with some DNS servers this is not a transient problem.
     * 
     * XXX Ideally we would perform A lookups only as far as needed. But as long
     * as we're looking up all the hosts, it would be better to look up the
     * least preferred host first, so that DNS lookup error messages make
     * more sense.
     * 
     * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX
     * hosts, whereas multiple A records per hostname must be used in the
     * order as received. They make the bogus assumption that a hostname with
     * multiple A records corresponds to one machine with multiple network
     * interfaces.
     * 
     * XXX 2821: Postfix recognizes the local machine by looking for its own IP
     * address in the list of mail exchangers. RFC 2821 says one has to look
     * at the mail exchanger hostname as well, making the bogus assumption
     * that an IP address is listed only under one hostname. However, looking
     * at hostnames provides a partial solution for MX hosts behind a NAT
     * gateway.
     */
    switch (dns_lookup(aname, T_MX, r, &mx_names, (VSTRING *) 0, why->reason)) {
    default:
	dsb_status(why, "4.4.3");
	if (var_ign_mx_lookup_err)
	    addr_list = smtp_host_addr(aname, misc_flags, why);
	break;
    case DNS_INVAL:
	dsb_status(why, "5.4.4");
	if (var_ign_mx_lookup_err)
	    addr_list = smtp_host_addr(aname, misc_flags, why);
	break;
    case DNS_NULLMX:
	dsb_status(why, "5.1.0");
	break;
    case DNS_POLICY:
	dsb_status(why, "4.7.0");
	break;
    case DNS_FAIL:
	dsb_status(why, "5.4.3");
	if (var_ign_mx_lookup_err)
	    addr_list = smtp_host_addr(aname, misc_flags, why);
	break;
    case DNS_OK:
	mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any);
	best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE);
	addr_list = smtp_addr_list(mx_names, why);
	if (mxrr)
	    *mxrr = dns_rr_copy(mx_names);	/* copies one record! */
	dns_rr_free(mx_names);
	if (addr_list == 0) {
	    /* Text does not change. */
	    if (var_smtp_defer_mxaddr) {
		/* Don't clobber the null terminator. */
		if (SMTP_HAS_HARD_DSN(why))
		    SMTP_SET_SOFT_DSN(why);	/* XXX */
		/* Require some error status. */
		else if (!SMTP_HAS_SOFT_DSN(why))
		    msg_panic("smtp_domain_addr: bad status");
	    }
	    msg_warn("no MX host for %s has a valid address record", name);
	    break;
	}
	best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE);
	if (msg_verbose)
	    smtp_print_addr(name, addr_list);
	if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
	    && (self = smtp_find_self(addr_list)) != 0) {
	    addr_list = smtp_truncate_self(addr_list, self->pref);
	    if (addr_list == 0) {
		if (best_pref != best_found) {
		    dsb_simple(why, "4.4.4",
			       "unable to find primary relay for %s", name);
		} else {
		    dsb_simple(why, "5.4.6", "mail for %s loops back to myself",
			       name);
		}
	    }
	}
#define SMTP_COMPARE_ADDR(flags) \
	(((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \
	 ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \
	 dns_rr_compare_pref_any)

	if (addr_list && addr_list->next && var_smtp_rand_addr) {
	    addr_list = dns_rr_shuffle(addr_list);
	    addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags));
	}
	break;
    case DNS_NOTFOUND:
	addr_list = smtp_host_addr(aname, misc_flags, why);
	break;
    }

    /*
     * Clean up.
     */
    *found_myself |= (self != 0);
    return (addr_list);
}