DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why) { DNS_RR *addr_list; dsb_reset(why); /* Paranoia */ /* * 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, host, 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); }
static void psc_service(VSTREAM *smtp_client_stream, char *unused_service, char **unused_argv) { /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This program handles all incoming connections, so it must not block. * We use event-driven code for all operations that introduce latency. * * Note: instead of using VSTREAM-level timeouts, we enforce limits on the * total amount of time to receive a complete SMTP command line. */ non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING); /* * Look up the remote SMTP client address and port. */ psc_endpt_lookup(smtp_client_stream, psc_endpt_lookup_done); }
void set_master_ent() { const char *myname = "set_master_ent"; char *disable; if (master_fp != 0) msg_panic("%s: configuration file still open", myname); if (master_path == 0) msg_panic("%s: no configuration file specified", myname); if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", master_path); master_line_last = 0; if (master_disable != 0) msg_panic("%s: service disable list still exists", myname); if (inet_proto_info()->ai_family_list[0] == 0) { msg_warn("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); msg_warn("disabling all type \"inet\" services in master.cf"); disable = concatenate(MASTER_XPORT_NAME_INET, ",", var_master_disable, (char *) 0); master_disable = match_service_init(disable); myfree(disable); } else master_disable = match_service_init(var_master_disable); }
void smtpd_peer_init(SMTPD_STATE *state) { /* * Initialize. */ if (proto_info == 0) proto_info = inet_proto_info(); /* * Prepare for partial initialization after error. */ memset((void *) &(state->sockaddr), 0, sizeof(state->sockaddr)); state->sockaddr_len = 0; state->name = 0; state->reverse_name = 0; state->addr = 0; state->namaddr = 0; state->rfc_addr = 0; state->port = 0; state->dest_addr = 0; state->dest_port = 0; /* * Determine the remote SMTP client address and port. * * XXX In stand-alone mode, don't assume that the peer will be a local * process. That could introduce a gaping hole when the SMTP daemon is * hooked up to the network via inetd or some other super-server. */ if (vstream_context(state->client) != 0) { smtpd_peer_from_pass_attr(state); if (*var_smtpd_uproxy_proto != 0) msg_warn("ignoring non-empty %s setting behind postscreen", VAR_SMTPD_UPROXY_PROTO); } else if (SMTPD_STAND_ALONE(state) || *var_smtpd_uproxy_proto == 0) { smtpd_peer_from_default(state); } else { smtpd_peer_from_proxy(state); } /* * Determine the remote SMTP client hostname. Note: some of the handlers * above provide surrogate endpoint information in case of error. In that * case, leave the surrogate information alone. */ if (state->name == 0) smtpd_peer_sockaddr_to_hostname(state); /* * Do the name[addr]:port formatting for pretty reports. */ state->namaddr = SMTPD_BUILD_NAMADDRPORT(state->name, state->addr, state->port); }
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); }
static void qmqpd_service(VSTREAM *stream, char *unused_service, char **argv) { QMQPD_STATE *state; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This routine runs when a client has connected to our network port. * Look up and sanitize the peer name and initialize some connection- * specific state. */ state = qmqpd_state_alloc(stream); /* * See if we need to turn on verbose logging for this client. */ debug_peer_check(state->name, state->addr); /* * Provide the QMQP service. */ msg_info("connect from %s", state->namaddr); qmqpd_proto(state); msg_info("disconnect from %s", state->namaddr); /* * After the client has gone away, clean up whatever we have set up at * connection time. */ debug_peer_restore(); qmqpd_state_free(state); }
void psc_endpt_lookup(VSTREAM *smtp_client_stream, PSC_ENDPT_LOOKUP_FN notify) { const PSC_ENDPT_LOOKUP_INFO *pp; if (proto_info == 0) proto_info = inet_proto_info(); for (pp = psc_endpt_lookup_info; /* see below */ ; pp++) { if (pp->name == 0) msg_fatal("unsupported %s value: %s", VAR_PSC_UPROXY_PROTO, var_psc_uproxy_proto); if (strcmp(var_psc_uproxy_proto, pp->name) == 0) { pp->endpt_lookup(smtp_client_stream, notify); return; } } }
static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, char *def_service) { DELIVER_REQUEST *request = state->request; SMTP_ITERATOR *iter = state->iterator; ARGV *sites; char *dest; char **cpp; int non_fallback_sites; int retry_plain = 0; DSN_BUF *why = state->why; /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) { dsb_simple(why, "4.4.4", "all network protocols are disabled"); return; } /* * Future proofing: do a null destination sanity check in case we allow * the primary destination to be a list (it could be just separators). */ sites = argv_alloc(1); argv_add(sites, nexthop, (char *) 0); if (sites->argc == 0) msg_panic("null destination: \"%s\"", nexthop); non_fallback_sites = sites->argc; argv_split_append(sites, var_fallback_relay, CHARS_COMMA_SP); /* * Don't give up after a hard host lookup error until we have tried the * fallback relay servers. * * Don't bounce mail after a host lookup problem with a relayhost or with a * fallback relay. * * Don't give up after a qualifying soft error until we have tried all * qualifying backup mail servers. * * All this means that error handling and error reporting depends on whether * the error qualifies for trying to deliver to a backup mail server, or * whether we're looking up a relayhost or fallback relay. The challenge * then is to build this into the pre-existing SMTP client without * getting lost in the complexity. */ #define IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites) \ (*(cpp) && (cpp) >= (sites)->argv + (non_fallback_sites)) for (cpp = sites->argv, (state->misc_flags |= SMTP_MISC_FLAG_FIRST_NEXTHOP); SMTP_RCPT_LEFT(state) > 0 && (dest = *cpp) != 0; cpp++, (state->misc_flags &= ~SMTP_MISC_FLAG_FIRST_NEXTHOP)) { char *dest_buf; char *domain; unsigned port; DNS_RR *addr_list; DNS_RR *addr; DNS_RR *next; int addr_count; int sess_count; SMTP_SESSION *session; int lookup_mx; unsigned domain_best_pref; MAI_HOSTADDR_STR hostaddr; if (cpp[1] == 0) state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; /* * Parse the destination. If no TCP port is specified, use the port * that is reserved for the protocol (SMTP or LMTP). */ dest_buf = smtp_parse_destination(dest, def_service, &domain, &port); if (var_helpful_warnings && var_smtp_tls_wrappermode == 0 && ntohs(port) == 465) { msg_info("SMTPS wrappermode (TCP port 465) requires setting " "\"%s = yes\", and \"%s = encrypt\" (or stronger)", VAR_LMTP_SMTP(TLS_WRAPPER), VAR_LMTP_SMTP(TLS_LEVEL)); } #define NO_HOST "" /* safety */ #define NO_ADDR "" /* safety */ SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state); /* * Resolve an SMTP or LMTP server. In the case of SMTP, skip mail * exchanger lookups when a quoted host is specified or when DNS * lookups are disabled. */ if (msg_verbose) msg_info("connecting to %s port %d", domain, ntohs(port)); if (smtp_mode) { if (ntohs(port) == IPPORT_SMTP) state->misc_flags |= SMTP_MISC_FLAG_LOOP_DETECT; else state->misc_flags &= ~SMTP_MISC_FLAG_LOOP_DETECT; lookup_mx = (smtp_dns_support != SMTP_DNS_DISABLED && *dest != '['); } else lookup_mx = 0; if (!lookup_mx) { addr_list = smtp_host_addr(domain, state->misc_flags, why); /* XXX We could be an MX host for this destination... */ } else { int i_am_mx = 0; addr_list = smtp_domain_addr(domain, &iter->mx, state->misc_flags, why, &i_am_mx); /* If we're MX host, don't connect to non-MX backups. */ if (i_am_mx) state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; } /* * Don't try fall-back hosts if mail loops to myself. That would just * make the problem worse. */ if (addr_list == 0 && SMTP_HAS_LOOP_DSN(why)) state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; /* * No early loop exit or we have a memory leak with dest_buf. */ if (addr_list) domain_best_pref = addr_list->pref; /* * When session caching is enabled, store the first good session for * this delivery request under the next-hop destination name. All * good sessions will be stored under their specific server IP * address. * * XXX smtp_session_cache_destinations specifies domain names without * :port, because : is already used for maptype:mapname. Because of * this limitation we use the bare domain without the optional [] or * non-default TCP port. * * Opportunistic (a.k.a. on-demand) session caching on request by the * queue manager. This is turned temporarily when a destination has a * high volume of mail in the active queue. When the surge reaches * its end, the queue manager requests that connections be retrieved * but not stored. */ if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)) { smtp_cache_policy(state, domain); if (state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE_MASK) SET_NEXTHOP_STATE(state, dest); } /* * Delete visited cached hosts from the address list. * * Optionally search the connection cache by domain name or by primary * MX address before we try to create new connections. * * Enforce the MX session and MX address counts per next-hop or * fall-back destination. smtp_reuse_session() will truncate the * address list when either limit is reached. */ if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD)) { if (state->cache_used->used > 0) smtp_scrub_addr_list(state->cache_used, &addr_list); sess_count = addr_count = smtp_reuse_session(state, &addr_list, domain_best_pref); } else sess_count = addr_count = 0; /* * Connect to an SMTP server: create primary MX connections, and * reuse or create backup MX connections. * * At the start of an SMTP session, all recipients are unmarked. In the * course of an SMTP session, recipients are marked as KEEP (deliver * to alternate mail server) or DROP (remove from recipient list). At * the end of an SMTP session, weed out the recipient list. Unmark * any left-over recipients and try to deliver them to a backup mail * server. * * Cache the first good session under the next-hop destination name. * Cache all good sessions under their physical endpoint. * * Don't query the session cache for primary MX hosts. We already did * that in smtp_reuse_session(), and if any were found in the cache, * they were already deleted from the address list. * * Currently, we use smtp_reuse_addr() only for SASL-unauthenticated * connections. Furthermore, we rely on smtp_reuse_addr() to look up * an existing SASL-unauthenticated connection only when a new * connection would be guaranteed not to require SASL authentication. * * In addition, we rely on smtp_reuse_addr() to look up an existing * plaintext connection only when a new connection would be * guaranteed not to use TLS. */ for (addr = addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) { next = addr->next; if (++addr_count == var_smtp_mxaddr_limit) next = 0; if (dns_rr_to_pa(addr, &hostaddr) == 0) { msg_warn("cannot convert type %s record to printable address", dns_strtype(addr->type)); /* XXX Assume there is no code at the end of this loop. */ continue; } vstring_strcpy(iter->addr, hostaddr.buf); vstring_strcpy(iter->host, SMTP_HNAME(addr)); iter->rr = addr; #ifdef USE_TLS if (!smtp_tls_policy_cache_query(why, state->tls, iter)) { msg_warn("TLS policy lookup for %s/%s: %s", STR(iter->dest), STR(iter->host), STR(why->reason)); continue; /* XXX Assume there is no code at the end of this loop. */ } if (var_smtp_tls_wrappermode && state->tls->level < TLS_LEV_ENCRYPT) { msg_warn("%s requires \"%s = encrypt\" (or stronger)", VAR_LMTP_SMTP(TLS_WRAPPER), VAR_LMTP_SMTP(TLS_LEVEL)); continue; /* XXX Assume there is no code at the end of this loop. */ } /* Disable TLS when retrying after a handshake failure */ if (retry_plain) { state->tls->level = TLS_LEV_NONE; retry_plain = 0; } #endif if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0 || addr->pref == domain_best_pref || !(session = smtp_reuse_addr(state, SMTP_KEY_MASK_SCACHE_ENDP_LABEL))) session = smtp_connect_addr(iter, why, state->misc_flags); if ((state->session = session) != 0) { session->state = state; #ifdef USE_TLS session->tls_nexthop = domain; #endif if (addr->pref == domain_best_pref) session->features |= SMTP_FEATURE_BEST_MX; /* Don't count handshake errors towards the session limit. */ if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) && next == 0) state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0 && smtp_helo(state) != 0) { #ifdef USE_TLS /* * When an opportunistic TLS handshake fails, try the * same address again, with TLS disabled. See also the * RETRY_AS_PLAINTEXT macro. */ if ((retry_plain = session->tls_retry_plain) != 0) { --addr_count; next = addr; } #endif /* * When a TLS handshake fails, the stream is marked * "dead" to avoid further I/O over a broken channel. */ if (!THIS_SESSION_IS_FORBIDDEN && vstream_ferror(session->stream) == 0 && vstream_feof(session->stream) == 0) smtp_quit(state); } else { /* Do count delivery errors towards the session limit. */ if (++sess_count == var_smtp_mxsess_limit) next = 0; if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) && next == 0) state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; smtp_xfer(state); #ifdef USE_TLS /* * When opportunistic TLS fails after the STARTTLS * handshake, try the same address again, with TLS * disabled. See also the RETRY_AS_PLAINTEXT macro. */ if ((retry_plain = session->tls_retry_plain) != 0) { --sess_count; --addr_count; next = addr; } #endif } smtp_cleanup_session(state); } else { /* The reason already includes the IP address and TCP port. */ msg_info("%s", STR(why->reason)); } /* XXX Code above assumes there is no code at this loop ending. */ } dns_rr_free(addr_list); if (iter->mx) { dns_rr_free(iter->mx); iter->mx = 0; /* Just in case */ } myfree(dest_buf); if (state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) break; } /* * We still need to deliver, bounce or defer some left-over recipients: * either mail loops or some backup mail server was unavailable. */ if (SMTP_RCPT_LEFT(state) > 0) { /* * In case of a "no error" indication we make up an excuse: we did * find the host address, but we did not attempt to connect to it. * This can happen when the fall-back relay was already tried via a * cached connection, so that the address list scrubber left behind * an empty list. */ if (!SMTP_HAS_DSN(why)) { dsb_simple(why, "4.3.0", "server unavailable or unable to receive mail"); } /* * Pay attention to what could be configuration problems, and pretend * that these are recoverable rather than bouncing the mail. */ else if (!SMTP_HAS_SOFT_DSN(why)) { /* * The fall-back destination did not resolve as expected, or it * is refusing to talk to us, or mail for it loops back to us. */ if (IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites)) { msg_warn("%s configuration problem", VAR_SMTP_FALLBACK); vstring_strcpy(why->status, "4.3.5"); /* XXX Keep the diagnostic code and MTA. */ } /* * The next-hop relayhost did not resolve as expected, or it is * refusing to talk to us, or mail for it loops back to us. * * XXX There is no equivalent safety net for mis-configured * sender-dependent relay hosts. The trivial-rewrite resolver * would have to flag the result, and the queue manager would * have to provide that information to delivery agents. */ else if (smtp_mode && strcmp(sites->argv[0], var_relayhost) == 0) { msg_warn("%s configuration problem", VAR_RELAYHOST); vstring_strcpy(why->status, "4.3.5"); /* XXX Keep the diagnostic code and MTA. */ } /* * Mail for the next-hop destination loops back to myself. Pass * the mail to the best_mx_transport or bounce it. */ else if (smtp_mode && SMTP_HAS_LOOP_DSN(why) && *var_bestmx_transp) { dsb_reset(why); /* XXX */ state->status = deliver_pass_all(MAIL_CLASS_PRIVATE, var_bestmx_transp, request); SMTP_RCPT_LEFT(state) = 0; /* XXX */ } } } /* * Cleanup. */ if (HAVE_NEXTHOP_STATE(state)) FREE_NEXTHOP_STATE(state); argv_free(sites); }
int inet_addr_host(INET_ADDR_LIST *addr_list, const char *hostname) { const char *myname = "inet_addr_host"; int sock; struct addrinfo *res0; struct addrinfo *res; int aierr; ssize_t hostnamelen; const char *hname; const char *serv; int initial_count = addr_list->used; INET_PROTO_INFO *proto_info; /* * The use of square brackets around an IPv6 addresses is required, even * though we don't enforce it as it'd make the code unnecessarily * complicated. * * XXX AIX 5.1 getaddrinfo() does not allow "0" as service, regardless of * whether or not a host is specified. */ if (*hostname == 0) { hname = 0; serv = "1"; } else if (*hostname == '[' && hostname[(hostnamelen = strlen(hostname)) - 1] == ']') { hname = mystrndup(hostname + 1, hostnamelen - 2); serv = 0; } else { hname = hostname; serv = 0; } proto_info = inet_proto_info(); if ((aierr = hostname_to_sockaddr(hname, serv, SOCK_STREAM, &res0)) == 0) { for (res = res0; res; res = res->ai_next) { /* * Safety net. */ if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { msg_info("%s: skipping address family %d for host \"%s\"", myname, res->ai_family, hostname); continue; } /* * On Linux systems it is not unusual for user-land to be out of * sync with kernel-land. When this is the case we try to be * helpful and filter out address families that the library * claims to understand but that are not supported by the kernel. */ if ((sock = socket(res->ai_family, SOCK_STREAM, 0)) < 0) { msg_warn("%s: skipping address family %d: %m", myname, res->ai_family); continue; } if (close(sock)) msg_warn("%s: close socket: %m", myname); inet_addr_list_append(addr_list, res->ai_addr); } freeaddrinfo(res0); } if (hname && hname != hostname) myfree((void *) hname); return (addr_list->used - initial_count); }
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); }
static void psc_service(VSTREAM *smtp_client_stream, char *unused_service, char **unused_argv) { const char *myname = "psc_service"; PSC_STATE *state; struct sockaddr_storage addr_storage; SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); MAI_HOSTADDR_STR smtp_client_addr; MAI_SERVPORT_STR smtp_client_port; MAI_HOSTADDR_STR smtp_server_addr; MAI_SERVPORT_STR smtp_server_port; int aierr; const char *stamp_str; int saved_flags; /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This program handles all incoming connections, so it must not block. * We use event-driven code for all operations that introduce latency. * * Note: instead of using VSTREAM-level timeouts, we enforce limits on the * total amount of time to receive a complete SMTP command line. */ non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING); /* * We use the event_server framework. This means we get already-accepted * connections so we have to invoke getpeername() to find out the remote * address and port. */ /* Best effort - if this non-blocking write(2) fails, so be it. */ #define PSC_SERVICE_DISCONNECT_AND_RETURN(stream) do { \ (void) write(vstream_fileno(stream), \ "421 4.3.2 No system resources\r\n", \ sizeof("421 4.3.2 No system resources\r\n") - 1); \ event_server_disconnect(stream); \ return; \ } while (0); /* * Look up the remote SMTP client address and port. */ if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *) & addr_storage, &addr_storage_len) < 0) { msg_warn("getpeername: %m -- dropping this connection"); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } /* * Convert the remote SMTP client address and port to printable form for * logging and access control. */ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, addr_storage_len, &smtp_client_addr, &smtp_client_port, 0)) != 0) { msg_warn("cannot convert client address/port to string: %s" " -- dropping this connection", MAI_STRERROR(aierr)); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0) memmove(smtp_client_addr.buf, smtp_client_addr.buf + 7, sizeof(smtp_client_addr.buf) - 7); if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d connect from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, smtp_client_addr.buf, smtp_client_port.buf); /* * Look up the local SMTP server address and port. */ if (getsockname(vstream_fileno(smtp_client_stream), (struct sockaddr *) & addr_storage, &addr_storage_len) < 0) { msg_warn("getsockname: %m -- dropping this connection"); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } /* * Convert the local SMTP server address and port to printable form for * logging and access control. */ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, addr_storage_len, &smtp_server_addr, &smtp_server_port, 0)) != 0) { msg_warn("cannot convert server address/port to string: %s" " -- dropping this connection", MAI_STRERROR(aierr)); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } if (strncasecmp("::ffff:", smtp_server_addr.buf, 7) == 0) memmove(smtp_server_addr.buf, smtp_server_addr.buf + 7, sizeof(smtp_server_addr.buf) - 7); msg_info("CONNECT from [%s]:%s to [%s]:%s", smtp_client_addr.buf, smtp_client_port.buf, smtp_server_addr.buf, smtp_server_port.buf); /* * Bundle up all the loose session pieces. This zeroes all flags and time * stamps. */ state = psc_new_session_state(smtp_client_stream, smtp_client_addr.buf, smtp_client_port.buf); /* * Reply with 421 when the client has too many open connections. */ if (var_psc_cconn_limit > 0 && state->client_concurrency > var_psc_cconn_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: too many connections", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.7.0 Error: too many connections\r\n"); return; } /* * Reply with 421 when we can't forward more connections. */ if (var_psc_post_queue_limit > 0 && psc_post_queue_length >= var_psc_post_queue_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: all server ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All server ports are busy\r\n"); return; } /* * The permanent white/blacklist has highest precedence. */ if (psc_acl != 0) { switch (psc_acl_eval(state, psc_acl, VAR_PSC_ACL)) { /* * Permanently blacklisted. */ case PSC_ACL_ACT_BLACKLIST: msg_info("BLACKLISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); switch (psc_blist_action) { case PSC_ACT_DROP: PSC_DROP_SESSION_STATE(state, "521 5.3.2 Service currently unavailable\r\n"); return; case PSC_ACT_ENFORCE: PSC_ENFORCE_SESSION_STATE(state, "550 5.3.2 Service currently unavailable\r\n"); break; case PSC_ACT_IGNORE: PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); /* * Not: PSC_PASS_SESSION_STATE. Repeat this test the next * time. */ break; default: msg_panic("%s: unknown blacklist action value %d", myname, psc_blist_action); } break; /* * Permanently whitelisted. */ case PSC_ACL_ACT_WHITELIST: msg_info("WHITELISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; /* * Other: dunno (don't know) or error. */ default: break; } } /* * The temporary whitelist (i.e. the postscreen cache) has the lowest * precedence. This cache contains information about the results of prior * tests. Whitelist the client when all enabled test results are still * valid. */ if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0 && psc_cache_map != 0 && (stamp_str = psc_cache_lookup(psc_cache_map, state->smtp_client_addr)) != 0) { saved_flags = state->flags; psc_parse_tests(state, stamp_str, event_time()); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: cached + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); if ((state->flags & PSC_STATE_MASK_ANY_TODO_FAIL) == 0) { msg_info("PASS OLD [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; } } else { saved_flags = state->flags; psc_new_tests(state); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: new + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); } /* * Don't whitelist clients that connect to backup MX addresses. Fail * "closed" on error. */ if (addr_match_list_match(psc_wlist_if, smtp_server_addr.buf) == 0) { state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD); msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); } /* * Reply with 421 when we can't analyze more connections. That also means * no deep protocol tests when the noforward flag is raised. */ if (var_psc_pre_queue_limit > 0 && psc_check_queue_length - psc_post_queue_length >= var_psc_pre_queue_limit) { msg_info("reject: connect from [%s]:%s: all screening ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All screening ports are busy\r\n"); return; } /* * If the client has no up-to-date results for some tests, do those tests * first. Otherwise, skip the tests and hand off the connection. */ if (state->flags & PSC_STATE_MASK_EARLY_TODO) psc_early_tests(state); else if (state->flags & (PSC_STATE_MASK_SMTPD_TODO | PSC_STATE_FLAG_NOFORWARD)) psc_smtpd_tests(state); else psc_conclude(state); }
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); }
static void own_inet_addr_init(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list) { INET_ADDR_LIST local_addrs; INET_ADDR_LIST local_masks; char *hosts; char *host; const char *sep = " \t,"; char *bufp; int nvirtual; int nlocal; MAI_HOSTADDR_STR hostaddr; struct sockaddr_storage *sa; struct sockaddr_storage *ma; inet_addr_list_init(addr_list); inet_addr_list_init(mask_list); /* * If we are listening on all interfaces (default), ask the system what * the interfaces are. */ if (strcmp(var_inet_interfaces, INET_INTERFACES_ALL) == 0) { if (inet_addr_local(addr_list, mask_list, inet_proto_info()->ai_family_list) == 0) msg_fatal("could not find any active network interfaces"); } /* * Select all loopback interfaces from the system's available interface * list. */ else if (strcmp(var_inet_interfaces, INET_INTERFACES_LOCAL) == 0) { inet_addr_list_init(&local_addrs); inet_addr_list_init(&local_masks); if (inet_addr_local(&local_addrs, &local_masks, inet_proto_info()->ai_family_list) == 0) msg_fatal("could not find any active network interfaces"); for (sa = local_addrs.addrs, ma = local_masks.addrs; sa < local_addrs.addrs + local_addrs.used; sa++, ma++) { if (sock_addr_in_loopback(SOCK_ADDR_PTR(sa))) { inet_addr_list_append(addr_list, SOCK_ADDR_PTR(sa)); inet_addr_list_append(mask_list, SOCK_ADDR_PTR(ma)); } } inet_addr_list_free(&local_addrs); inet_addr_list_free(&local_masks); } /* * If we are supposed to be listening only on specific interface * addresses (virtual hosting), look up the addresses of those * interfaces. */ else { bufp = hosts = mystrdup(var_inet_interfaces); while ((host = mystrtok(&bufp, sep)) != 0) if (inet_addr_host(addr_list, host) == 0) msg_fatal("config variable %s: host not found: %s", VAR_INET_INTERFACES, host); myfree(hosts); /* * Weed out duplicate IP addresses. Duplicates happen when the same * IP address is listed under multiple hostnames. If we don't weed * out duplicates, Postfix can suddenly stop working after the DNS is * changed. */ inet_addr_list_uniq(addr_list); /* * Find out the netmask for each virtual interface, by looking it up * among all the local interfaces. */ inet_addr_list_init(&local_addrs); inet_addr_list_init(&local_masks); if (inet_addr_local(&local_addrs, &local_masks, inet_proto_info()->ai_family_list) == 0) msg_fatal("could not find any active network interfaces"); for (nvirtual = 0; nvirtual < addr_list->used; nvirtual++) { for (nlocal = 0; /* see below */ ; nlocal++) { if (nlocal >= local_addrs.used) { SOCKADDR_TO_HOSTADDR( SOCK_ADDR_PTR(addr_list->addrs + nvirtual), SOCK_ADDR_LEN(addr_list->addrs + nvirtual), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); msg_fatal("parameter %s: no local interface found for %s", VAR_INET_INTERFACES, hostaddr.buf); } if (SOCK_ADDR_EQ_ADDR(addr_list->addrs + nvirtual, local_addrs.addrs + nlocal)) { inet_addr_list_append(mask_list, SOCK_ADDR_PTR(local_masks.addrs + nlocal)); break; } } } inet_addr_list_free(&local_addrs); inet_addr_list_free(&local_masks); } }
static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, char *def_service) { DELIVER_REQUEST *request = state->request; ARGV *sites; char *dest; char **cpp; int non_fallback_sites; int retry_plain = 0; DSN_BUF *why = state->why; /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) { dsb_simple(why, "4.4.4", "all network protocols are disabled"); return; } /* * First try to deliver to the indicated destination, then try to deliver * to the optional fall-back relays. * * Future proofing: do a null destination sanity check in case we allow the * primary destination to be a list (it could be just separators). */ sites = argv_alloc(1); argv_add(sites, nexthop, (char *) 0); if (sites->argc == 0) msg_panic("null destination: \"%s\"", nexthop); non_fallback_sites = sites->argc; if ((state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) == 0) argv_split_append(sites, var_fallback_relay, ", \t\r\n"); /* * Don't give up after a hard host lookup error until we have tried the * fallback relay servers. * * Don't bounce mail after a host lookup problem with a relayhost or with a * fallback relay. * * Don't give up after a qualifying soft error until we have tried all * qualifying backup mail servers. * * All this means that error handling and error reporting depends on whether * the error qualifies for trying to deliver to a backup mail server, or * whether we're looking up a relayhost or fallback relay. The challenge * then is to build this into the pre-existing SMTP client without * getting lost in the complexity. */ #define IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites) \ (*(cpp) && (cpp) >= (sites)->argv + (non_fallback_sites)) for (cpp = sites->argv, (state->misc_flags |= SMTP_MISC_FLAG_FIRST_NEXTHOP); SMTP_RCPT_LEFT(state) > 0 && (dest = *cpp) != 0; cpp++, (state->misc_flags &= ~SMTP_MISC_FLAG_FIRST_NEXTHOP)) { char *dest_buf; char *domain; unsigned port; DNS_RR *addr_list; DNS_RR *addr; DNS_RR *next; int addr_count; int sess_count; SMTP_SESSION *session; int lookup_mx; unsigned domain_best_pref; MAI_HOSTADDR_STR hostaddr; if (cpp[1] == 0) state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; /* * Parse the destination. Default is to use the SMTP port. Look up * the address instead of the mail exchanger when a quoted host is * specified, or when DNS lookups are disabled. */ dest_buf = smtp_parse_destination(dest, def_service, &domain, &port); if (var_helpful_warnings && ntohs(port) == 465) { msg_info("CLIENT wrappermode (port smtps/465) is unimplemented"); msg_info("instead, send to (port submission/587) with STARTTLS"); } /* * Resolve an SMTP server. Skip mail exchanger lookups when a quoted * host is specified, or when DNS lookups are disabled. */ if (msg_verbose) msg_info("connecting to %s port %d", domain, ntohs(port)); if ((state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) == 0) { if (ntohs(port) == IPPORT_SMTP) state->misc_flags |= SMTP_MISC_FLAG_LOOP_DETECT; else state->misc_flags &= ~SMTP_MISC_FLAG_LOOP_DETECT; lookup_mx = (var_disable_dns == 0 && *dest != '['); } else lookup_mx = 0; if (!lookup_mx) { addr_list = smtp_host_addr(domain, state->misc_flags, why); /* XXX We could be an MX host for this destination... */ } else { int i_am_mx = 0; addr_list = smtp_domain_addr(domain, state->misc_flags, why, &i_am_mx); /* If we're MX host, don't connect to non-MX backups. */ if (i_am_mx) state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; } /* * Don't try fall-back hosts if mail loops to myself. That would just * make the problem worse. */ if (addr_list == 0 && SMTP_HAS_LOOP_DSN(why)) state->misc_flags |= SMTP_MISC_FLAG_FINAL_NEXTHOP; /* * No early loop exit or we have a memory leak with dest_buf. */ if (addr_list) domain_best_pref = addr_list->pref; /* * When session caching is enabled, store the first good session for * this delivery request under the next-hop destination name. All * good sessions will be stored under their specific server IP * address. * * XXX Replace sites->argv by (lookup_mx, domain, port) triples so we * don't have to make clumsy ad-hoc copies and keep track of who * free()s the memory. * * XXX smtp_session_cache_destinations specifies domain names without * :port, because : is already used for maptype:mapname. Because of * this limitation we use the bare domain without the optional [] or * non-default TCP port. * * Opportunistic (a.k.a. on-demand) session caching on request by the * queue manager. This is turned temporarily when a destination has a * high volume of mail in the active queue. * * XXX Disable connection caching when sender-dependent authentication * is enabled. We must not send someone elses mail over an * authenticated connection, and we must not send mail that requires * authentication over a connection that wasn't authenticated. */ if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)) { smtp_cache_policy(state, domain); if (state->misc_flags & SMTP_MISC_FLAG_CONN_STORE) SET_NEXTHOP_STATE(state, lookup_mx, domain, port); } /* * Delete visited cached hosts from the address list. * * Optionally search the connection cache by domain name or by primary * MX address before we try to create new connections. * * Enforce the MX session and MX address counts per next-hop or * fall-back destination. smtp_reuse_session() will truncate the * address list when either limit is reached. */ if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD)) { if (state->cache_used->used > 0) smtp_scrub_addr_list(state->cache_used, &addr_list); sess_count = addr_count = smtp_reuse_session(state, lookup_mx, domain, port, &addr_list, domain_best_pref); } else sess_count = addr_count = 0; /* * Connect to an SMTP server: create primary MX connections, and * reuse or create backup MX connections. * * At the start of an SMTP session, all recipients are unmarked. In the * course of an SMTP session, recipients are marked as KEEP (deliver * to alternate mail server) or DROP (remove from recipient list). At * the end of an SMTP session, weed out the recipient list. Unmark * any left-over recipients and try to deliver them to a backup mail * server. * * Cache the first good session under the next-hop destination name. * Cache all good sessions under their physical endpoint. * * Don't query the session cache for primary MX hosts. We already did * that in smtp_reuse_session(), and if any were found in the cache, * they were already deleted from the address list. */ for (addr = addr_list; SMTP_RCPT_LEFT(state) > 0 && addr; addr = next) { next = addr->next; if (++addr_count == var_smtp_mxaddr_limit) next = 0; if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0 || addr->pref == domain_best_pref || dns_rr_to_pa(addr, &hostaddr) == 0 || !(session = smtp_reuse_addr(state, hostaddr.buf, port))) session = smtp_connect_addr(dest, addr, port, why, state->misc_flags); if ((state->session = session) != 0) { session->state = state; if (addr->pref == domain_best_pref) session->features |= SMTP_FEATURE_BEST_MX; /* Don't count handshake errors towards the session limit. */ if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) && next == 0) state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; #ifdef USE_TLS /* Disable TLS when retrying after a handshake failure */ if (retry_plain) { if (session->tls_level >= TLS_LEV_ENCRYPT) msg_panic("Plain-text retry wrong for mandatory TLS"); session->tls_level = TLS_LEV_NONE; retry_plain = 0; } session->tls_nexthop = domain; /* for TLS_LEV_SECURE */ #endif if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0 && smtp_helo(state) != 0) { #ifdef USE_TLS /* * When an opportunistic TLS handshake fails, try the * same address again, with TLS disabled. See also the * RETRY_AS_PLAINTEXT macro. */ if ((retry_plain = session->tls_retry_plain) != 0) { --addr_count; next = addr; } #endif /* * When a TLS handshake fails, the stream is marked * "dead" to avoid further I/O over a broken channel. */ if (!THIS_SESSION_IS_DEAD && vstream_ferror(session->stream) == 0 && vstream_feof(session->stream) == 0) smtp_quit(state); } else { /* Do count delivery errors towards the session limit. */ if (++sess_count == var_smtp_mxsess_limit) next = 0; if ((state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) && next == 0) state->misc_flags |= SMTP_MISC_FLAG_FINAL_SERVER; smtp_xfer(state); } smtp_cleanup_session(state); } else { /* The reason already includes the IP address and TCP port. */ msg_info("%s", STR(why->reason)); } /* Insert: test if we must skip the remaining MX hosts. */ } dns_rr_free(addr_list); myfree(dest_buf); if (state->misc_flags & SMTP_MISC_FLAG_FINAL_NEXTHOP) break; } /* * We still need to deliver, bounce or defer some left-over recipients: * either mail loops or some backup mail server was unavailable. */ if (SMTP_RCPT_LEFT(state) > 0) { /* * In case of a "no error" indication we make up an excuse: we did * find the host address, but we did not attempt to connect to it. * This can happen when the fall-back relay was already tried via a * cached connection, so that the address list scrubber left behind * an empty list. */ if (!SMTP_HAS_DSN(why)) { dsb_simple(why, "4.3.0", "server unavailable or unable to receive mail"); } /* * Pay attention to what could be configuration problems, and pretend * that these are recoverable rather than bouncing the mail. */ else if (!SMTP_HAS_SOFT_DSN(why) && (state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) == 0) { /* * The fall-back destination did not resolve as expected, or it * is refusing to talk to us, or mail for it loops back to us. */ if (IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites)) { msg_warn("%s configuration problem", VAR_SMTP_FALLBACK); vstring_strcpy(why->status, "4.3.5"); /* XXX Keep the diagnostic code and MTA. */ } /* * The next-hop relayhost did not resolve as expected, or it is * refusing to talk to us, or mail for it loops back to us. */ else if (strcmp(sites->argv[0], var_relayhost) == 0) { msg_warn("%s configuration problem", VAR_RELAYHOST); vstring_strcpy(why->status, "4.3.5"); /* XXX Keep the diagnostic code and MTA. */ } /* * Mail for the next-hop destination loops back to myself. Pass * the mail to the best_mx_transport or bounce it. */ else if (SMTP_HAS_LOOP_DSN(why) && *var_bestmx_transp) { dsb_reset(why); /* XXX */ state->status = deliver_pass_all(MAIL_CLASS_PRIVATE, var_bestmx_transp, request); SMTP_RCPT_LEFT(state) = 0; /* XXX */ } } } /* * Cleanup. */ if (HAVE_NEXTHOP_STATE(state)) FREE_NEXTHOP_STATE(state); argv_free(sites); }
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); }