xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/proxyp.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: proxyp.c,v 1.2 2021/08/14 16:14:58 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2000-2020 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: proxyp.c,v 1.2 2021/08/14 16:14:58 christos Exp $");
20 
21 #include "portable.h"
22 #include "slap.h"
23 
24 #ifdef HAVE_STDINT_H
25 #include <stdint.h>
26 #endif
27 #ifdef HAVE_INTTYPES_H
28 #include <inttypes.h>
29 #endif
30 
31 #include <lber_types.h>
32 #include <ac/string.h>
33 #include <ac/errno.h>
34 
35 typedef struct {
36 	uint8_t  sig[12];	/* hex 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a */
37 	uint8_t  ver_cmd;	/* protocol version and command */
38 	uint8_t  fam;		/* protocol family and address */
39 	uint16_t len;		/* length of address data */
40 } proxyp_header;
41 
42 typedef union {
43 	struct {	/* for TCP/UDP over IPv4, len = 12 */
44 		uint32_t src_addr;
45 		uint32_t dst_addr;
46 		uint16_t src_port;
47 		uint16_t dst_port;
48 	} ip4;
49 	struct {	/* for TCP/UDP over IPv6, len = 36 */
50 		uint8_t  src_addr[16];
51 		uint8_t  dst_addr[16];
52 		uint16_t src_port;
53 		uint16_t dst_port;
54 	} ip6;
55 	struct {	/* for AF_UNIX sockets, len = 216 */
56 		uint8_t src_addr[108];
57 		uint8_t dst_addr[108];
58 	} unx;
59 } proxyp_addr;
60 
61 static const uint8_t proxyp_sig[12] = {
62 	0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a,
63 };
64 
65 int
proxyp(ber_socket_t sfd,Sockaddr * from)66 proxyp( ber_socket_t sfd, Sockaddr *from ) {
67 	proxyp_header pph;
68 	proxyp_addr ppa;
69 	char peername[LDAP_IPADDRLEN];
70 	struct berval peerbv = BER_BVC(peername);
71 	/* Maximum size of header minus static component size is max option size */
72 	uint8_t proxyp_options[536 - 16];
73 	int pph_len;
74 	int ret;
75 
76 	peername[0] = '\0';
77 
78 	do {
79 		ret = tcp_read( SLAP_FD2SOCK( sfd ), &pph, sizeof(pph) );
80 	} while ( ret == -1 && errno == EINTR );
81 
82 	if ( ret == -1 ) {
83 		char ebuf[128];
84 		int save_errno = errno;
85 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
86 				"header read failed %d (%s)\n",
87 				(long)sfd, save_errno,
88 				AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
89 		return 0;
90 	} else if ( ret != sizeof(pph) ) {
91 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
92 				"header read insufficient data %d\n",
93 				(long)sfd, ret );
94 		return 0;
95 	}
96 
97 	if ( memcmp( pph.sig, proxyp_sig, 12 ) != 0 ) {
98 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
99 				"invalid header signature\n", (long)sfd );
100 		return 0;
101 	}
102 
103 	if ( ( pph.ver_cmd & 0xF0 ) != 0x20 ) {
104 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
105 				"invalid header version %x\n",
106 				(long)sfd, pph.ver_cmd & 0xF0 );
107 		return 0;
108 	}
109 
110 	pph_len = ntohs( pph.len );
111 	if ( ( pph.ver_cmd & 0x0F ) == 0x01 ) { /* PROXY command */
112 		int addr_len;
113 		switch ( pph.fam ) {
114 		case 0x11: /* TCPv4 */
115 			addr_len = sizeof( ppa.ip4 );
116 			break;
117 		case 0x21: /* TCPv6 */
118 			addr_len = sizeof( ppa.ip6 );
119 			break;
120 		default:
121 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
122 					"unsupported protocol %x\n",
123 					(long)sfd, pph.fam );
124 			return 0;
125 		}
126 
127 		if ( pph_len < addr_len ) {
128 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
129 					"address length %d too small, expecting %d\n",
130 					(long)sfd, pph_len, addr_len );
131 			return 0;
132 		}
133 
134 		do {
135 			ret = tcp_read( SLAP_FD2SOCK (sfd), &ppa, addr_len );
136 		} while ( ret == -1 && errno == EINTR );
137 
138 		if ( ret == -1 ) {
139 			char ebuf[128];
140 			int save_errno = errno;
141 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
142 					"address read failed %d (%s)\n",
143 					(long)sfd, save_errno,
144 					AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
145 			return 0;
146 		} else if ( ret != addr_len ) {
147 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
148 					"address read insufficient data, expecting %d, read %d\n",
149 					(long)sfd, addr_len, ret );
150 			return 0;
151 		}
152 
153 		pph_len -= addr_len;
154 	}
155 
156 	switch ( pph.ver_cmd & 0x0F ) {
157 	case 0x01: /* PROXY command */
158 		switch ( pph.fam ) {
159 		case 0x11: /* TCPv4 */
160 			ldap_pvt_sockaddrstr( from, &peerbv );
161 			Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
162 					(long)sfd, peername );
163 
164 			from->sa_in_addr.sin_family = AF_INET;
165 			from->sa_in_addr.sin_addr.s_addr = ppa.ip4.src_addr;
166 			from->sa_in_addr.sin_port = ppa.ip4.src_port;
167 			break;
168 
169 		case 0x21: /* TCPv6 */
170 #ifdef LDAP_PF_INET6
171 			ldap_pvt_sockaddrstr( from, &peerbv );
172 			Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
173 					(long)sfd, peername );
174 			from->sa_in6_addr.sin6_family = AF_INET6;
175 			memcpy( &from->sa_in6_addr.sin6_addr, ppa.ip6.src_addr,
176 					sizeof(ppa.ip6.src_addr) );
177 			from->sa_in6_addr.sin6_port = ppa.ip6.src_port;
178 #else
179 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
180 					"IPv6 proxied addresses disabled\n",
181 					(long)sfd );
182 			return 0;
183 #endif
184 			break;
185 		}
186 
187 		break;
188 
189 	case 0x00: /* LOCAL command */
190 		Debug( LDAP_DEBUG_CONNS, "proxyp(%ld): "
191 				"local connection, ignoring proxy data\n",
192 				(long)sfd );
193 		break;
194 
195 	default:
196 		Debug( LDAP_DEBUG_ANY, "proxyp(%ld): invalid command %x\n",
197 				(long)sfd, pph.ver_cmd & 0x0F );
198 		return 0;
199 	}
200 
201 	/* Clear out any options left in proxy packet */
202 	if ( pph_len > 0 ) {
203 		if (pph_len > sizeof( proxyp_options ) ) {
204 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
205 					"options size %d too big\n",
206 					(long)sfd, pph_len );
207 			return 0;
208 		}
209 
210 		do {
211 			ret = tcp_read( SLAP_FD2SOCK (sfd), &proxyp_options, pph_len );
212 		} while ( ret == -1 && errno == EINTR );
213 
214 		if ( ret == -1 ) {
215 			char ebuf[128];
216 			int save_errno = errno;
217 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
218 					"options read failed %d (%s)\n",
219 					(long)sfd, save_errno,
220 					AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
221 			return 0;
222 		} else if ( ret != pph_len ) {
223 			Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
224 					"options read insufficient data, expecting %d, read %d\n",
225 					(long)sfd, pph_len, ret );
226 			return 0;
227 		}
228 	}
229 
230 	return 1;
231 }
232