static void smtpd_peer_hostaddr_to_sockaddr(SMTPD_STATE *state) { const char *myname = "smtpd_peer_hostaddr_to_sockaddr"; struct addrinfo *res; int aierr; if ((aierr = hostaddr_to_sockaddr(state->addr, state->port, SOCK_STREAM, &res)) != 0) msg_fatal("%s: cannot convert client address/port to string: %s", myname, MAI_STRERROR(aierr)); if (res->ai_addrlen > sizeof(state->sockaddr)) msg_panic("%s: address length > struct sockaddr_storage", myname); memcpy((void *) &(state->sockaddr), res->ai_addr, res->ai_addrlen); state->sockaddr_len = res->ai_addrlen; freeaddrinfo(res); }
static void smtp_update_addr_list(DNS_RR **addr_list, const char *server_addr, int session_count) { DNS_RR *addr; DNS_RR *next; int aierr; struct addrinfo *res0; if (*addr_list == 0) return; /* * Truncate the address list if we are not going to use it anyway. */ if (session_count == var_smtp_mxsess_limit || session_count == var_smtp_mxaddr_limit) { dns_rr_free(*addr_list); *addr_list = 0; return; } /* * Convert server address to internal form, and look it up in the address * list. * * XXX smtp_reuse_session() breaks if we remove two or more adjacent list * elements but do not truncate the list to zero length. * * XXX Extend the SMTP_SESSION structure with sockaddr information so that * we can avoid repeated string->binary transformations for the same * address. */ if ((aierr = hostaddr_to_sockaddr(server_addr, (char *) 0, 0, &res0)) != 0) { msg_warn("hostaddr_to_sockaddr %s: %s", server_addr, MAI_STRERROR(aierr)); } else { for (addr = *addr_list; addr; addr = next) { next = addr->next; if (DNS_RR_EQ_SA(addr, (struct sockaddr *) res0->ai_addr)) { *addr_list = dns_rr_remove(*addr_list, addr); break; } } freeaddrinfo(res0); } }
static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain, const char *addr) { const char *myname = "dnsblog_query"; ARGV *octets; int i; struct addrinfo *res; unsigned char *ipv6_addr; int dns_status; DNS_RR *addr_list; DNS_RR *rr; MAI_HOSTADDR_STR hostaddr; if (msg_verbose) msg_info("%s: addr %s dnsbl_domain %s", myname, addr, dnsbl_domain); VSTRING_RESET(query); /* * Reverse the client IPV6 address, represented as 32 hexadecimal * nibbles. We use the binary address to avoid tricky code. Asking for an * AAAA record makes no sense here. Just like with IPv4 we use the lookup * result as a bit mask, not as an IP address. */ #ifdef HAS_IPV6 if (valid_ipv6_hostaddr(addr, DONT_GRIPE)) { if (hostaddr_to_sockaddr(addr, (char *) 0, 0, &res) != 0 || res->ai_family != PF_INET6) msg_fatal("%s: unable to convert address %s", myname, addr); ipv6_addr = (unsigned char *) &SOCK_ADDR_IN6_ADDR(res->ai_addr); for (i = sizeof(SOCK_ADDR_IN6_ADDR(res->ai_addr)) - 1; i >= 0; i--) vstring_sprintf_append(query, "%x.%x.", ipv6_addr[i] & 0xf, ipv6_addr[i] >> 4); freeaddrinfo(res); } else #endif /* * Reverse the client IPV4 address, represented as four decimal octet * values. We use the textual address for convenience. */ {
static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why, int sess_flags) { const char *myname = "smtp_connect_addr"; struct sockaddr_storage ss; /* remote */ struct sockaddr *sa = (struct sockaddr *) &ss; SOCKADDR_SIZE salen = sizeof(ss); MAI_HOSTADDR_STR hostaddr; DNS_RR *addr = iter->rr; unsigned port = iter->port; int sock; char *bind_addr; char *bind_var; dsb_reset(why); /* Paranoia */ /* * Sanity checks. */ if (dns_rr_to_sa(addr, port, sa, &salen) != 0) { msg_warn("%s: skip address type %s: %m", myname, dns_strtype(addr->type)); dsb_simple(why, "4.4.0", "network address conversion failed: %m"); return (0); } /* * Initialize. */ if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) msg_fatal("%s: socket: %m", myname); if (inet_windowsize > 0) set_inet_windowsize(sock, inet_windowsize); /* * Allow the sysadmin to specify the source address, for example, as "-o * smtp_bind_address=x.x.x.x" in the master.cf file. */ #ifdef HAS_IPV6 if (sa->sa_family == AF_INET6) { bind_addr = var_smtp_bind_addr6; bind_var = VAR_LMTP_SMTP(BIND_ADDR6); } else #endif if (sa->sa_family == AF_INET) { bind_addr = var_smtp_bind_addr; bind_var = VAR_LMTP_SMTP(BIND_ADDR); } else bind_var = bind_addr = ""; if (*bind_addr) { int aierr; struct addrinfo *res0; if ((aierr = hostaddr_to_sockaddr(bind_addr, (char *) 0, 0, &res0)) != 0) msg_fatal("%s: bad %s parameter: %s: %s", myname, bind_var, bind_addr, MAI_STRERROR(aierr)); if (bind(sock, res0->ai_addr, res0->ai_addrlen) < 0) msg_warn("%s: bind %s: %m", myname, bind_addr); else if (msg_verbose) msg_info("%s: bind %s", myname, bind_addr); freeaddrinfo(res0); } /* * When running as a virtual host, bind to the virtual interface so that * the mail appears to come from the "right" machine address. * * XXX The IPv6 patch expands the null host (as client endpoint) and uses * the result as the loopback address list. */ else { int count = 0; struct sockaddr *own_addr = 0; INET_ADDR_LIST *addr_list = own_inet_addr_list(); struct sockaddr_storage *s; for (s = addr_list->addrs; s < addr_list->addrs + addr_list->used; s++) { if (SOCK_ADDR_FAMILY(s) == sa->sa_family) { if (count++ > 0) break; own_addr = SOCK_ADDR_PTR(s); } } if (count == 1 && !sock_addr_in_loopback(own_addr)) { if (bind(sock, own_addr, SOCK_ADDR_LEN(own_addr)) < 0) { SOCKADDR_TO_HOSTADDR(own_addr, SOCK_ADDR_LEN(own_addr), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); msg_warn("%s: bind %s: %m", myname, hostaddr.buf); } else if (msg_verbose) { SOCKADDR_TO_HOSTADDR(own_addr, SOCK_ADDR_LEN(own_addr), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); msg_info("%s: bind %s", myname, hostaddr.buf); } } } /* * Connect to the server. */ if (msg_verbose) msg_info("%s: trying: %s[%s] port %d...", myname, STR(iter->host), STR(iter->addr), ntohs(port)); return (smtp_connect_sock(sock, sa, salen, iter, why, sess_flags)); }
void smtpd_peer_init(SMTPD_STATE *state) { const char *myname = "smtpd_peer_init"; SOCKADDR_SIZE sa_length; struct sockaddr *sa; INET_PROTO_INFO *proto_info = inet_proto_info(); sa = (struct sockaddr *) & (state->sockaddr); sa_length = sizeof(state->sockaddr); /* * Look up the peer address information. * * XXX If we make local endpoint (getsockname) information available to * Milter applications as {if_name} and {if_addr}, then we also must be * able to provide this via the XCLIENT command for Milter testing. * * XXX If we make local or remote port information available to policy * servers or Milter applications, then we must also make this testable * with the XCLIENT command, otherwise there will be confusion. * * XXX If we make local or remote port information available via logging, * then we must also support these attributes with the XFORWARD command. * * XXX If support were to be added for Milter applications in down-stream * MTAs, then consistency demands that we propagate a lot of Sendmail * macro information via the XFORWARD command. Otherwise we could end up * with a very confusing situation. */ if (getpeername(vstream_fileno(state->client), sa, &sa_length) >= 0) { errno = 0; } /* * If peer went away, give up. */ if (errno != 0 && errno != ENOTSOCK) { state->name = mystrdup(CLIENT_NAME_UNKNOWN); state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); state->addr = mystrdup(CLIENT_ADDR_UNKNOWN); state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN); state->addr_family = AF_UNSPEC; state->name_status = SMTPD_PEER_CODE_PERM; state->reverse_name_status = SMTPD_PEER_CODE_PERM; state->port = mystrdup(CLIENT_PORT_UNKNOWN); } /* * Convert the client address to printable address and hostname. * * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, while * Postfix IPv6 (or IPv4) support is turned off, don't (skip to the final * else clause, pretend the origin is localhost[127.0.0.1], and become an * open relay). */ else if (errno == 0 && (sa->sa_family == AF_INET #ifdef AF_INET6 || sa->sa_family == AF_INET6 #endif )) { MAI_HOSTNAME_STR client_name; MAI_HOSTADDR_STR client_addr; MAI_SERVPORT_STR client_port; int aierr; char *colonp; /* * Sanity check: we can't use sockets that we're not configured for. */ if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0) msg_fatal("cannot handle socket type %s with \"%s = %s\"", #ifdef AF_INET6 sa->sa_family == AF_INET6 ? "AF_INET6" : #endif sa->sa_family == AF_INET ? "AF_INET" : "other", VAR_INET_PROTOCOLS, var_inet_protocols); /* * Sorry, but there are some things that we just cannot do while * connected to the network. */ if (geteuid() != var_owner_uid || getuid() != var_owner_uid) { msg_error("incorrect SMTP server privileges: uid=%lu euid=%lu", (unsigned long) getuid(), (unsigned long) geteuid()); msg_fatal("the Postfix SMTP server must run with $%s privileges", VAR_MAIL_OWNER); } /* * Convert the client address to printable form. */ if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr, &client_port, 0)) != 0) msg_fatal("%s: cannot convert client address/port to string: %s", myname, MAI_STRERROR(aierr)); state->port = mystrdup(client_port.buf); /* * XXX Strip off the IPv6 datalink suffix to avoid false alarms with * strict address syntax checks. */ #ifdef HAS_IPV6 (void) split_at(client_addr.buf, '%'); #endif /* * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on, * but only if IPv4 support is enabled (why would anyone want to turn * it off)? With IPv4 support enabled we have no need for the IPv6 * form in logging, hostname verification and access checks. */ #ifdef HAS_IPV6 if (sa->sa_family == AF_INET6) { if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0 && IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa)) && (colonp = strrchr(client_addr.buf, ':')) != 0) { struct addrinfo *res0; if (msg_verbose > 1) msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"", myname, client_addr.buf, colonp + 1); state->addr = mystrdup(colonp + 1); state->rfc_addr = mystrdup(colonp + 1); state->addr_family = AF_INET; aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0); if (aierr) msg_fatal("%s: cannot convert %s from string to binary: %s", myname, state->addr, MAI_STRERROR(aierr)); sa_length = res0->ai_addrlen; if (sa_length > sizeof(state->sockaddr)) sa_length = sizeof(state->sockaddr); memcpy((char *) sa, res0->ai_addr, sa_length); freeaddrinfo(res0); /* 200412 */ } /* * Following RFC 2821 section 4.1.3, an IPv6 address literal gets * a prefix of 'IPv6:'. We do this consistently for all IPv6 * addresses that that appear in headers or envelopes. The fact * that valid_mailhost_addr() enforces the form helps of course. * We use the form without IPV6: prefix when doing access * control, or when accessing the connection cache. */ else { state->addr = mystrdup(client_addr.buf); state->rfc_addr = concatenate(IPV6_COL, client_addr.buf, (char *) 0); state->addr_family = sa->sa_family; } } /* * An IPv4 address is in dotted quad decimal form. */ else #endif { state->addr = mystrdup(client_addr.buf); state->rfc_addr = mystrdup(client_addr.buf); state->addr_family = sa->sa_family; } /* * Look up and sanity check the client hostname. * * It is unsafe to allow numeric hostnames, especially because there * exists pressure to turn off the name->addr double check. In that * case an attacker could trivally bypass access restrictions. * * sockaddr_to_hostname() already rejects malformed or numeric names. */ #define TEMP_AI_ERROR(e) \ ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM) #define REJECT_PEER_NAME(state, code) { \ myfree(state->name); \ state->name = mystrdup(CLIENT_NAME_UNKNOWN); \ state->name_status = code; \ } if (var_smtpd_peername_lookup == 0) { state->name = mystrdup(CLIENT_NAME_UNKNOWN); state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); state->name_status = SMTPD_PEER_CODE_PERM; state->reverse_name_status = SMTPD_PEER_CODE_PERM; } else if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name, (MAI_SERVNAME_STR *) 0, 0)) != 0) { state->name = mystrdup(CLIENT_NAME_UNKNOWN); state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN); state->name_status = (TEMP_AI_ERROR(aierr) ? SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM); state->reverse_name_status = (TEMP_AI_ERROR(aierr) ? SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM); } else { struct addrinfo *res0; struct addrinfo *res; state->name = mystrdup(client_name.buf); state->reverse_name = mystrdup(client_name.buf); state->name_status = SMTPD_PEER_CODE_OK; state->reverse_name_status = SMTPD_PEER_CODE_OK; /* * Reject the hostname if it does not list the peer address. * Without further validation or qualification, such information * must not be allowed to enter the audit trail, as people would * draw false conclusions. */ aierr = hostname_to_sockaddr_pf(state->name, state->addr_family, (char *) 0, 0, &res0); if (aierr) { msg_warn("%s: hostname %s verification failed: %s", state->addr, state->name, MAI_STRERROR(aierr)); REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ? SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_FORGED)); } else { for (res = res0; /* void */ ; res = res->ai_next) { if (res == 0) { msg_warn("%s: address not listed for hostname %s", state->addr, state->name); REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED); break; } if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { msg_info("skipping address family %d for host %s", res->ai_family, state->name); continue; } if (sock_addr_cmp_addr(res->ai_addr, sa) == 0) break; /* keep peer name */ } freeaddrinfo(res0); } } } /* * If it's not Internet, assume the client is local, and avoid using the * naming service because that can hang when the machine is disconnected. */ else { state->name = mystrdup("localhost"); state->reverse_name = mystrdup("localhost"); state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */ state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */ state->addr_family = AF_UNSPEC; state->name_status = SMTPD_PEER_CODE_OK; state->reverse_name_status = SMTPD_PEER_CODE_OK; state->port = mystrdup("0"); /* XXX bogus. */ } /* * Do the name[addr]:port formatting for pretty reports. */ state->namaddr = SMTPD_BUILD_NAMADDRPORT(state->name, state->addr, state->port); }
int resolve_local(const char *addr) { char *saved_addr = mystrdup(addr); char *dest; const char *bare_dest; struct addrinfo *res0 = 0; ssize_t len; /* * The optimizer will eliminate tests that always fail. */ #define RETURN(x) \ do { \ myfree(saved_addr); \ if (res0) \ freeaddrinfo(res0); \ return(x); \ } while (0) if (resolve_local_list == 0) resolve_local_init(); /* * Strip one trailing dot but not dot-dot. * * XXX This should not be distributed all over the code. Problem is, * addresses can enter the system via multiple paths: networks, local * forward/alias/include files, even as the result of address rewriting. */ len = strlen(saved_addr); if (len == 0) RETURN(0); if (saved_addr[len - 1] == '.') saved_addr[--len] = 0; if (len == 0 || saved_addr[len - 1] == '.') RETURN(0); /* * Compare the destination against the list of destinations that we * consider local. */ if (string_list_match(resolve_local_list, saved_addr)) RETURN(1); if (resolve_local_list->error != 0) RETURN(resolve_local_list->error); /* * Compare the destination against the list of interface addresses that * we are supposed to listen on. * * The destination may be an IPv6 address literal that was buried somewhere * inside a deeply recursively nested address. This information comes * from an untrusted source, and Wietse is not confident that everyone's * getaddrinfo() etc. implementation is sufficiently robust. The syntax * is complex enough with null field compression and with IPv4-in-IPv6 * addresses that errors are likely. * * The solution below is ad-hoc. We neutralize the string as soon as we * realize that its contents could be harmful. We neutralize the string * here, instead of neutralizing it in every resolve_local() caller. * That's because resolve_local knows how the address is going to be * parsed and converted into binary form. * * There are several more structural solutions to this. * * - One solution is to disallow address literals. This is not as bad as it * seems: I have never seen actual legitimate use of address literals. * * - Another solution is to label each string with a trustworthiness label * and to expect that all Postfix infrastructure will exercise additional * caution when given a string with untrusted content. This is not likely * to happen. * * FIX 200501 IPv6 patch did not require "IPv6:" prefix in numerical * addresses. */ dest = saved_addr; if (*dest == '[' && dest[len - 1] == ']') { dest++; dest[len -= 2] = 0; if ((bare_dest = valid_mailhost_addr(dest, DO_GRIPE)) != 0 && hostaddr_to_sockaddr(bare_dest, (char *) 0, 0, &res0) == 0) { if (own_inet_addr(res0->ai_addr) || proxy_inet_addr(res0->ai_addr)) RETURN(1); } } /* * Must be remote, or a syntax error. */ RETURN(0); }
static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state) { const char *myname = "smtpd_peer_sockaddr_to_hostaddr"; struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr); SOCKADDR_SIZE sa_length = state->sockaddr_len; /* * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, * while Postfix IPv6 (or IPv4) support is turned off, don't (skip to the * final else clause, pretend the origin is localhost[127.0.0.1], and * become an open relay). */ if (sa->sa_family == AF_INET #ifdef AF_INET6 || sa->sa_family == AF_INET6 #endif ) { MAI_HOSTADDR_STR client_addr; MAI_SERVPORT_STR client_port; int aierr; char *colonp; /* * Sanity check: we can't use sockets that we're not configured for. */ if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0) msg_fatal("cannot handle socket type %s with \"%s = %s\"", #ifdef AF_INET6 sa->sa_family == AF_INET6 ? "AF_INET6" : #endif sa->sa_family == AF_INET ? "AF_INET" : "other", VAR_INET_PROTOCOLS, var_inet_protocols); /* * Sorry, but there are some things that we just cannot do while * connected to the network. */ if (geteuid() != var_owner_uid || getuid() != var_owner_uid) { msg_error("incorrect SMTP server privileges: uid=%lu euid=%lu", (unsigned long) getuid(), (unsigned long) geteuid()); msg_fatal("the Postfix SMTP server must run with $%s privileges", VAR_MAIL_OWNER); } /* * Convert the client address to printable form. */ if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr, &client_port, 0)) != 0) msg_fatal("%s: cannot convert client address/port to string: %s", myname, MAI_STRERROR(aierr)); state->port = mystrdup(client_port.buf); /* * XXX Require that the infrastructure strips off the IPv6 datalink * suffix to avoid false alarms with strict address syntax checks. */ #ifdef HAS_IPV6 if (strchr(client_addr.buf, '%') != 0) msg_panic("%s: address %s has datalink suffix", myname, client_addr.buf); #endif /* * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on, * but only if IPv4 support is enabled (why would anyone want to turn * it off)? With IPv4 support enabled we have no need for the IPv6 * form in logging, hostname verification and access checks. */ #ifdef HAS_IPV6 if (sa->sa_family == AF_INET6) { if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0 && IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa)) && (colonp = strrchr(client_addr.buf, ':')) != 0) { struct addrinfo *res0; if (msg_verbose > 1) msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"", myname, client_addr.buf, colonp + 1); state->addr = mystrdup(colonp + 1); state->rfc_addr = mystrdup(colonp + 1); state->addr_family = AF_INET; aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0); if (aierr) msg_fatal("%s: cannot convert %s from string to binary: %s", myname, state->addr, MAI_STRERROR(aierr)); sa_length = res0->ai_addrlen; if (sa_length > sizeof(state->sockaddr)) sa_length = sizeof(state->sockaddr); memcpy((void *) sa, res0->ai_addr, sa_length); freeaddrinfo(res0); /* 200412 */ } /* * Following RFC 2821 section 4.1.3, an IPv6 address literal gets * a prefix of 'IPv6:'. We do this consistently for all IPv6 * addresses that that appear in headers or envelopes. The fact * that valid_mailhost_addr() enforces the form helps of course. * We use the form without IPV6: prefix when doing access * control, or when accessing the connection cache. */ else { state->addr = mystrdup(client_addr.buf); state->rfc_addr = concatenate(IPV6_COL, client_addr.buf, (char *) 0); state->addr_family = sa->sa_family; } } /* * An IPv4 address is in dotted quad decimal form. */ else #endif { state->addr = mystrdup(client_addr.buf); state->rfc_addr = mystrdup(client_addr.buf); state->addr_family = sa->sa_family; } return (0); } /* * It's not Internet. */ else { return (-1); } }
static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host, unsigned pref, DSN_BUF *why) { const char *myname = "smtp_addr_one"; DNS_RR *addr = 0; DNS_RR *rr; int aierr; struct addrinfo *res0; struct addrinfo *res; INET_PROTO_INFO *proto_info = inet_proto_info(); int found; if (msg_verbose) msg_info("%s: host %s", myname, host); /* * Interpret a numerical name as an address. */ if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0 && strchr((char *) proto_info->sa_family_list, res0->ai_family) != 0) { if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0) msg_fatal("host %s: conversion error for address family %d: %m", host, ((struct sockaddr *) (res0->ai_addr))->sa_family); addr_list = dns_rr_append(addr_list, addr); freeaddrinfo(res0); return (addr_list); } /* * Use DNS lookup, but keep the option open to use native name service. * * XXX A soft error dominates past and future hard errors. Therefore we * should not clobber a soft error text and status code. */ if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) { switch (dns_lookup_v(host, smtp_dns_res_opt, &addr, (VSTRING *) 0, why->reason, DNS_REQ_FLAG_NONE, proto_info->dns_atype_list)) { case DNS_OK: for (rr = addr; rr; rr = rr->next) rr->pref = pref; addr_list = dns_rr_append(addr_list, addr); return (addr_list); default: dsb_status(why, "4.4.3"); return (addr_list); case DNS_FAIL: dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3"); return (addr_list); case DNS_INVAL: dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4"); return (addr_list); case DNS_NOTFOUND: dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4"); /* maybe native naming service will succeed */ break; } } /* * Use the native name service which also looks in /etc/hosts. * * XXX A soft error dominates past and future hard errors. Therefore we * should not clobber a soft error text and status code. */ #define RETRY_AI_ERROR(e) \ ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM) #ifdef EAI_NODATA #define DSN_NOHOST(e) \ ((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME) #else #define DSN_NOHOST(e) \ ((e) == EAI_AGAIN || (e) == EAI_NONAME) #endif if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) { if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) { dsb_simple(why, (SMTP_HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ? (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") : (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"), "unable to look up host %s: %s", host, MAI_STRERROR(aierr)); } else { for (found = 0, res = res0; res != 0; res = res->ai_next) { if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { msg_info("skipping address family %d for host %s", res->ai_family, host); continue; } found++; if ((addr = dns_sa_to_rr(host, pref, res->ai_addr)) == 0) msg_fatal("host %s: conversion error for address family %d: %m", host, ((struct sockaddr *) (res0->ai_addr))->sa_family); addr_list = dns_rr_append(addr_list, addr); } freeaddrinfo(res0); if (found == 0) { dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4", "%s: host not found", host); } return (addr_list); } } /* * No further alternatives for host lookup. */ return (addr_list); }
void qmqpd_peer_init(QMQPD_STATE *state) { const char *myname = "qmqpd_peer_init"; struct sockaddr_storage ss; struct sockaddr *sa; SOCKADDR_SIZE sa_length; INET_PROTO_INFO *proto_info = inet_proto_info(); sa = (struct sockaddr *) & ss; sa_length = sizeof(ss); /* * Look up the peer address information. */ if (getpeername(vstream_fileno(state->client), sa, &sa_length) >= 0) { errno = 0; } /* * If peer went away, give up. */ if (errno != 0 && errno != ENOTSOCK) { state->name = mystrdup(CLIENT_NAME_UNKNOWN); state->addr = mystrdup(CLIENT_ADDR_UNKNOWN); state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN); state->addr_family = AF_UNSPEC; state->port = mystrdup(CLIENT_PORT_UNKNOWN); } /* * Convert the client address to printable address and hostname. * * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, while * Postfix IPv6 (or IPv4) support is turned off, don't (skip to the final * else clause, pretend the origin is localhost[127.0.0.1], and become an * open relay). */ else if (errno == 0 && (sa->sa_family == AF_INET #ifdef AF_INET6 || sa->sa_family == AF_INET6 #endif )) { MAI_HOSTNAME_STR client_name; MAI_HOSTADDR_STR client_addr; MAI_SERVPORT_STR client_port; int aierr; char *colonp; /* * Sanity check: we can't use sockets that we're not configured for. */ if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0) msg_fatal("cannot handle socket type %s with \"%s = %s\"", #ifdef AF_INET6 sa->sa_family == AF_INET6 ? "AF_INET6" : #endif sa->sa_family == AF_INET ? "AF_INET" : "other", VAR_INET_PROTOCOLS, var_inet_protocols); /* * Sorry, but there are some things that we just cannot do while * connected to the network. */ if (geteuid() != var_owner_uid || getuid() != var_owner_uid) { msg_error("incorrect QMQP server privileges: uid=%lu euid=%lu", (unsigned long) getuid(), (unsigned long) geteuid()); msg_fatal("the Postfix QMQP server must run with $%s privileges", VAR_MAIL_OWNER); } /* * Convert the client address to printable form. */ if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr, &client_port, 0)) != 0) msg_fatal("%s: cannot convert client address/port to string: %s", myname, MAI_STRERROR(aierr)); state->port = mystrdup(client_port.buf); /* * XXX Require that the infrastructure strips off the IPv6 datalink * suffix to avoid false alarms with strict address syntax checks. */ #ifdef HAS_IPV6 if (strchr(client_addr.buf, '%') != 0) msg_panic("%s: address %s has datalink suffix", myname, client_addr.buf); #endif /* * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on, * but only if IPv4 support is enabled (why would anyone want to turn * it off)? With IPv4 support enabled we have no need for the IPv6 * form in logging, hostname verification and access checks. */ #ifdef HAS_IPV6 if (sa->sa_family == AF_INET6) { if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0 && IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa)) && (colonp = strrchr(client_addr.buf, ':')) != 0) { struct addrinfo *res0; if (msg_verbose > 1) msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"", myname, client_addr.buf, colonp + 1); state->addr = mystrdup(colonp + 1); state->rfc_addr = mystrdup(colonp + 1); state->addr_family = AF_INET; aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0); if (aierr) msg_fatal("%s: cannot convert %s from string to binary: %s", myname, state->addr, MAI_STRERROR(aierr)); sa_length = res0->ai_addrlen; if (sa_length > sizeof(ss)) sa_length = sizeof(ss); memcpy((char *) sa, res0->ai_addr, sa_length); freeaddrinfo(res0); } /* * Following RFC 2821 section 4.1.3, an IPv6 address literal gets * a prefix of 'IPv6:'. We do this consistently for all IPv6 * addresses that that appear in headers or envelopes. The fact * that valid_mailhost_addr() enforces the form helps of course. * We use the form without IPV6: prefix when doing access * control, or when accessing the connection cache. */ else { state->addr = mystrdup(client_addr.buf); state->rfc_addr = concatenate(IPV6_COL, client_addr.buf, (char *) 0); state->addr_family = sa->sa_family; } } /* * An IPv4 address is in dotted quad decimal form. */ else #endif { state->addr = mystrdup(client_addr.buf); state->rfc_addr = mystrdup(client_addr.buf); state->addr_family = sa->sa_family; } /* * Look up and sanity check the client hostname. * * It is unsafe to allow numeric hostnames, especially because there * exists pressure to turn off the name->addr double check. In that * case an attacker could trivally bypass access restrictions. * * sockaddr_to_hostname() already rejects malformed or numeric names. */ #define REJECT_PEER_NAME(state) { \ myfree(state->name); \ state->name = mystrdup(CLIENT_NAME_UNKNOWN); \ } if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name, (MAI_SERVNAME_STR *) 0, 0)) != 0) { state->name = mystrdup(CLIENT_NAME_UNKNOWN); } else { struct addrinfo *res0; struct addrinfo *res; state->name = mystrdup(client_name.buf); /* * Reject the hostname if it does not list the peer address. */ aierr = hostname_to_sockaddr_pf(state->name, state->addr_family, (char *) 0, 0, &res0); if (aierr) { msg_warn("hostname %s does not resolve to address %s: %s", state->name, state->addr, MAI_STRERROR(aierr)); REJECT_PEER_NAME(state); } else { for (res = res0; /* void */ ; res = res->ai_next) { if (res == 0) { msg_warn("hostname %s does not resolve to address %s", state->addr, state->name); REJECT_PEER_NAME(state); break; } if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { msg_info("skipping address family %d for host %s", res->ai_family, state->name); continue; } if (sock_addr_cmp_addr(res->ai_addr, sa) == 0) break; /* keep peer name */ } freeaddrinfo(res0); } } } /* * If it's not Internet, assume the client is local, and avoid using the * naming service because that can hang when the machine is disconnected. */ else { state->name = mystrdup("localhost"); state->addr = mystrdup("127.0.0.1"); /* XXX bogus. */ state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */ state->addr_family = AF_UNSPEC; state->port = mystrdup("0"); /* XXX bogus. */ } /* * Do the name[addr]:port formatting for pretty reports. */ state->namaddr = concatenate(state->name, "[", state->addr, "]", var_qmqpd_client_port_log ? ":" : (char *) 0, state->port, (char *) 0); }