Home | History | Annotate | Line # | Download | only in qmqpd
      1 /*	$NetBSD: qmqpd_peer.c,v 1.4 2025/02/25 19:15:49 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	qmqpd_peer 3
      6 /* SUMMARY
      7 /*	look up peer name/address information
      8 /* SYNOPSIS
      9 /*	#include "qmqpd.h"
     10 /*
     11 /*	void	qmqpd_peer_init(state)
     12 /*	QMQPD_STATE *state;
     13 /*
     14 /*	void	qmqpd_peer_reset(state)
     15 /*	QMQPD_STATE *state;
     16 /* DESCRIPTION
     17 /*	The qmqpd_peer_init() routine attempts to produce a printable
     18 /*	version of the peer name and address of the specified socket.
     19 /*	Where information is unavailable, the name and/or address
     20 /*	are set to "unknown".
     21 /*
     22 /*	qmqpd_peer_init() updates the following fields:
     23 /* .IP name
     24 /*	The client hostname. An unknown name is represented by the
     25 /*	string "unknown".
     26 /* .IP addr
     27 /*	Printable representation of the client address.
     28 /* .IP namaddr
     29 /*	String of the form: "name[addr]:port".
     30 /* .PP
     31 /*	qmqpd_peer_reset() releases memory allocated by qmqpd_peer_init().
     32 /* LICENSE
     33 /* .ad
     34 /* .fi
     35 /*	The Secure Mailer license must be distributed with this software.
     36 /* AUTHOR(S)
     37 /*	Wietse Venema
     38 /*	IBM T.J. Watson Research
     39 /*	P.O. Box 704
     40 /*	Yorktown Heights, NY 10598, USA
     41 /*
     42 /*	Wietse Venema
     43 /*	Google, Inc.
     44 /*	111 8th Avenue
     45 /*	New York, NY 10011, USA
     46 /*--*/
     47 
     48 /* System library. */
     49 
     50 #include <sys_defs.h>
     51 #include <sys/socket.h>
     52 #include <netinet/in.h>
     53 #include <arpa/inet.h>
     54 #include <errno.h>
     55 #include <netdb.h>
     56 #include <string.h>
     57 
     58 /* Utility library. */
     59 
     60 #include <msg.h>
     61 #include <mymalloc.h>
     62 #include <stringops.h>
     63 #include <myaddrinfo.h>
     64 #include <sock_addr.h>
     65 #include <inet_proto.h>
     66 #include <split_at.h>
     67 
     68 /* Global library. */
     69 
     70 #include <mail_proto.h>
     71 #include <valid_mailhost_addr.h>
     72 #include <mail_params.h>
     73 
     74 /* Application-specific. */
     75 
     76 #include "qmqpd.h"
     77 
     78 /* qmqpd_peer_init - initialize peer information */
     79 
     80 void    qmqpd_peer_init(QMQPD_STATE *state)
     81 {
     82     const char *myname = "qmqpd_peer_init";
     83     struct sockaddr_storage ss;
     84     struct sockaddr *sa;
     85     SOCKADDR_SIZE sa_length;
     86     const INET_PROTO_INFO *proto_info = inet_proto_info();
     87 
     88     sa = (struct sockaddr *) &ss;
     89     sa_length = sizeof(ss);
     90 
     91     /*
     92      * Look up the peer address information.
     93      */
     94     if (getpeername(vstream_fileno(state->client), sa, &sa_length) >= 0) {
     95 	errno = 0;
     96     }
     97 
     98     /*
     99      * If peer went away, give up.
    100      */
    101     if (errno != 0 && errno != ENOTSOCK) {
    102 	state->name = mystrdup(CLIENT_NAME_UNKNOWN);
    103 	state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
    104 	state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
    105 	state->addr_family = AF_UNSPEC;
    106 	state->port = mystrdup(CLIENT_PORT_UNKNOWN);
    107     }
    108 
    109     /*
    110      * Convert the client address to printable address and hostname.
    111      *
    112      * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, while
    113      * Postfix IPv6 (or IPv4) support is turned off, don't (skip to the final
    114      * else clause, pretend the origin is localhost[127.0.0.1], and become an
    115      * open relay).
    116      */
    117     else if (errno == 0
    118 	     && (sa->sa_family == AF_INET
    119 #ifdef AF_INET6
    120 		 || sa->sa_family == AF_INET6
    121 #endif
    122 		 )) {
    123 	MAI_HOSTNAME_STR client_name;
    124 	MAI_HOSTADDR_STR client_addr;
    125 	MAI_SERVPORT_STR client_port;
    126 	int     aierr;
    127 	char   *colonp;
    128 
    129 	/*
    130 	 * Sanity check: we can't use sockets that we're not configured for.
    131 	 */
    132 	if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0)
    133 	    msg_fatal("cannot handle socket type %s with \"%s = %s\"",
    134 #ifdef AF_INET6
    135 		      sa->sa_family == AF_INET6 ? "AF_INET6" :
    136 #endif
    137 		      sa->sa_family == AF_INET ? "AF_INET" :
    138 		      "other", VAR_INET_PROTOCOLS, var_inet_protocols);
    139 
    140 	/*
    141 	 * Sorry, but there are some things that we just cannot do while
    142 	 * connected to the network.
    143 	 */
    144 	if (geteuid() != var_owner_uid || getuid() != var_owner_uid) {
    145 	    msg_error("incorrect QMQP server privileges: uid=%lu euid=%lu",
    146 		      (unsigned long) getuid(), (unsigned long) geteuid());
    147 	    msg_fatal("the Postfix QMQP server must run with $%s privileges",
    148 		      VAR_MAIL_OWNER);
    149 	}
    150 
    151 	/*
    152 	 * Convert the client address to printable form.
    153 	 */
    154 	if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
    155 					  &client_port, 0)) != 0)
    156 	    msg_fatal("%s: cannot convert client address/port to string: %s",
    157 		      myname, MAI_STRERROR(aierr));
    158 	state->port = mystrdup(client_port.buf);
    159 
    160 	/*
    161 	 * XXX Require that the infrastructure strips off the IPv6 datalink
    162 	 * suffix to avoid false alarms with strict address syntax checks.
    163 	 */
    164 #ifdef HAS_IPV6
    165 	if (strchr(client_addr.buf, '%') != 0)
    166 	    msg_panic("%s: address %s has datalink suffix",
    167 		      myname, client_addr.buf);
    168 #endif
    169 
    170 	/*
    171 	 * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
    172 	 * but only if IPv4 support is enabled (why would anyone want to turn
    173 	 * it off)? With IPv4 support enabled we have no need for the IPv6
    174 	 * form in logging, hostname verification and access checks.
    175 	 */
    176 #ifdef HAS_IPV6
    177 	if (sa->sa_family == AF_INET6) {
    178 	    if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0
    179 		&& IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa))
    180 		&& (colonp = strrchr(client_addr.buf, ':')) != 0) {
    181 		struct addrinfo *res0;
    182 
    183 		if (msg_verbose > 1)
    184 		    msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"",
    185 			     myname, client_addr.buf, colonp + 1);
    186 
    187 		state->addr = mystrdup(colonp + 1);
    188 		state->rfc_addr = mystrdup(colonp + 1);
    189 		state->addr_family = AF_INET;
    190 		aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0);
    191 		if (aierr)
    192 		    msg_fatal("%s: cannot convert %s from string to binary: %s",
    193 			      myname, state->addr, MAI_STRERROR(aierr));
    194 		sa_length = res0->ai_addrlen;
    195 		if (sa_length > sizeof(ss))
    196 		    sa_length = sizeof(ss);
    197 		memcpy((void *) sa, res0->ai_addr, sa_length);
    198 		freeaddrinfo(res0);
    199 	    }
    200 
    201 	    /*
    202 	     * Following RFC 2821 section 4.1.3, an IPv6 address literal gets
    203 	     * a prefix of 'IPv6:'. We do this consistently for all IPv6
    204 	     * addresses that appear in headers or envelopes. The fact that
    205 	     * valid_mailhost_addr() enforces the form helps of course. We
    206 	     * use the form without IPV6: prefix when doing access control,
    207 	     * or when accessing the connection cache.
    208 	     */
    209 	    else {
    210 		state->addr = mystrdup(client_addr.buf);
    211 		state->rfc_addr =
    212 		    concatenate(IPV6_COL, client_addr.buf, (char *) 0);
    213 		state->addr_family = sa->sa_family;
    214 	    }
    215 	}
    216 
    217 	/*
    218 	 * An IPv4 address is in dotted quad decimal form.
    219 	 */
    220 	else
    221 #endif
    222 	{
    223 	    state->addr = mystrdup(client_addr.buf);
    224 	    state->rfc_addr = mystrdup(client_addr.buf);
    225 	    state->addr_family = sa->sa_family;
    226 	}
    227 
    228 	/*
    229 	 * Look up and sanity check the client hostname.
    230 	 *
    231 	 * It is unsafe to allow numeric hostnames, especially because there
    232 	 * exists pressure to turn off the name->addr double check. In that
    233 	 * case an attacker could trivally bypass access restrictions.
    234 	 *
    235 	 * sockaddr_to_hostname() already rejects malformed or numeric names.
    236 	 */
    237 #define REJECT_PEER_NAME(state) { \
    238 	myfree(state->name); \
    239 	state->name = mystrdup(CLIENT_NAME_UNKNOWN); \
    240     }
    241 
    242 	if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name,
    243 					  (MAI_SERVNAME_STR *) 0, 0)) != 0) {
    244 	    state->name = mystrdup(CLIENT_NAME_UNKNOWN);
    245 	} else {
    246 	    struct addrinfo *res0;
    247 	    struct addrinfo *res;
    248 
    249 	    state->name = mystrdup(client_name.buf);
    250 
    251 	    /*
    252 	     * Reject the hostname if it does not list the peer address.
    253 	     */
    254 	    aierr = hostname_to_sockaddr_pf(state->name, state->addr_family,
    255 					    (char *) 0, 0, &res0);
    256 	    if (aierr) {
    257 		msg_warn("hostname %s does not resolve to address %s: %s",
    258 			 state->name, state->addr, MAI_STRERROR(aierr));
    259 		REJECT_PEER_NAME(state);
    260 	    } else {
    261 		for (res = res0; /* void */ ; res = res->ai_next) {
    262 		    if (res == 0) {
    263 			msg_warn("hostname %s does not resolve to address %s",
    264 				 state->addr, state->name);
    265 			REJECT_PEER_NAME(state);
    266 			break;
    267 		    }
    268 		    if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
    269 			msg_info("skipping address family %d for host %s",
    270 				 res->ai_family, state->name);
    271 			continue;
    272 		    }
    273 		    if (sock_addr_cmp_addr(res->ai_addr, sa) == 0)
    274 			break;			/* keep peer name */
    275 		}
    276 		freeaddrinfo(res0);
    277 	    }
    278 	}
    279     }
    280 
    281     /*
    282      * If it's not Internet, assume the client is local, and avoid using the
    283      * naming service because that can hang when the machine is disconnected.
    284      */
    285     else {
    286 	state->name = mystrdup("localhost");
    287 	state->addr = mystrdup("127.0.0.1");	/* XXX bogus. */
    288 	state->rfc_addr = mystrdup("127.0.0.1");/* XXX bogus. */
    289 	state->addr_family = AF_UNSPEC;
    290 	state->port = mystrdup("0");		/* XXX bogus. */
    291     }
    292 
    293     /*
    294      * Do the name[addr]:port formatting for pretty reports.
    295      */
    296     state->namaddr =
    297 	concatenate(state->name, "[", state->addr, "]",
    298 		    var_qmqpd_client_port_log ? ":" : (char *) 0,
    299 		    state->port, (char *) 0);
    300 }
    301 
    302 /* qmqpd_peer_reset - destroy peer information */
    303 
    304 void    qmqpd_peer_reset(QMQPD_STATE *state)
    305 {
    306     myfree(state->name);
    307     myfree(state->addr);
    308     myfree(state->namaddr);
    309     myfree(state->rfc_addr);
    310     myfree(state->port);
    311 }
    312