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